diff --git a/AudioServer.sln b/AudioServer.sln new file mode 100644 index 0000000..c08c396 --- /dev/null +++ b/AudioServer.sln @@ -0,0 +1,51 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.34031.279 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PulseAudio", "PulseAudio\PulseAudio.vcxproj", "{567419EA-59C6-4393-BBD3-D3850887AF8B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM = Debug|ARM + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM = Release|ARM + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {567419EA-59C6-4393-BBD3-D3850887AF8B}.Debug|ARM.ActiveCfg = Debug|ARM + {567419EA-59C6-4393-BBD3-D3850887AF8B}.Debug|ARM.Build.0 = Debug|ARM + {567419EA-59C6-4393-BBD3-D3850887AF8B}.Debug|ARM.Deploy.0 = Debug|ARM + {567419EA-59C6-4393-BBD3-D3850887AF8B}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {567419EA-59C6-4393-BBD3-D3850887AF8B}.Debug|ARM64.Build.0 = Debug|ARM64 + {567419EA-59C6-4393-BBD3-D3850887AF8B}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {567419EA-59C6-4393-BBD3-D3850887AF8B}.Debug|x64.ActiveCfg = Debug|x64 + {567419EA-59C6-4393-BBD3-D3850887AF8B}.Debug|x64.Build.0 = Debug|x64 + {567419EA-59C6-4393-BBD3-D3850887AF8B}.Debug|x64.Deploy.0 = Debug|x64 + {567419EA-59C6-4393-BBD3-D3850887AF8B}.Debug|x86.ActiveCfg = Debug|x86 + {567419EA-59C6-4393-BBD3-D3850887AF8B}.Debug|x86.Build.0 = Debug|x86 + {567419EA-59C6-4393-BBD3-D3850887AF8B}.Debug|x86.Deploy.0 = Debug|x86 + {567419EA-59C6-4393-BBD3-D3850887AF8B}.Release|ARM.ActiveCfg = Release|ARM + {567419EA-59C6-4393-BBD3-D3850887AF8B}.Release|ARM.Build.0 = Release|ARM + {567419EA-59C6-4393-BBD3-D3850887AF8B}.Release|ARM.Deploy.0 = Release|ARM + {567419EA-59C6-4393-BBD3-D3850887AF8B}.Release|ARM64.ActiveCfg = Release|ARM64 + {567419EA-59C6-4393-BBD3-D3850887AF8B}.Release|ARM64.Build.0 = Release|ARM64 + {567419EA-59C6-4393-BBD3-D3850887AF8B}.Release|ARM64.Deploy.0 = Release|ARM64 + {567419EA-59C6-4393-BBD3-D3850887AF8B}.Release|x64.ActiveCfg = Release|x64 + {567419EA-59C6-4393-BBD3-D3850887AF8B}.Release|x64.Build.0 = Release|x64 + {567419EA-59C6-4393-BBD3-D3850887AF8B}.Release|x64.Deploy.0 = Release|x64 + {567419EA-59C6-4393-BBD3-D3850887AF8B}.Release|x86.ActiveCfg = Release|x86 + {567419EA-59C6-4393-BBD3-D3850887AF8B}.Release|x86.Build.0 = Release|x86 + {567419EA-59C6-4393-BBD3-D3850887AF8B}.Release|x86.Deploy.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {1D344C9D-0E37-48CC-839F-E0B52A42FD1F} + EndGlobalSection +EndGlobal diff --git a/PulseAudio/3rd/Observer.h b/PulseAudio/3rd/Observer.h new file mode 100644 index 0000000..174ed46 --- /dev/null +++ b/PulseAudio/3rd/Observer.h @@ -0,0 +1,8 @@ +#pragma once +class Subject; + +class Observer +{ +public: + virtual void update(Subject*) = 0; +}; \ No newline at end of file diff --git a/PulseAudio/3rd/Subject.cpp b/PulseAudio/3rd/Subject.cpp new file mode 100644 index 0000000..259f7d4 --- /dev/null +++ b/PulseAudio/3rd/Subject.cpp @@ -0,0 +1,30 @@ +#include "Subject.h" +#include "Observer.h" + +Subject::Subject() +{ +} + +Subject::~Subject() +{ + if (!mp_Observers.empty()) + mp_Observers.clear(); +} + +void Subject::addObserver(Observer* obs) +{ + mp_Observers.push_back(obs); +} + +void Subject::delObserver(Observer* obs) +{ + mp_Observers.remove(obs); +} + +void Subject::notify(Subject* sub) +{ + for (auto i : mp_Observers) + { + i->update(sub); + } +} \ No newline at end of file diff --git a/PulseAudio/3rd/Subject.h b/PulseAudio/3rd/Subject.h new file mode 100644 index 0000000..2ad5ef1 --- /dev/null +++ b/PulseAudio/3rd/Subject.h @@ -0,0 +1,20 @@ +#pragma once +#include +class Observer; + +class Subject +{ +public: + Subject(); + virtual ~Subject(); + + virtual void addObserver(Observer*); + virtual void delObserver(Observer*); + +protected: + + virtual void notify(Subject*); + +private: + std::list mp_Observers; +}; \ No newline at end of file diff --git a/PulseAudio/3rd/vban.cpp b/PulseAudio/3rd/vban.cpp new file mode 100644 index 0000000..9428abc --- /dev/null +++ b/PulseAudio/3rd/vban.cpp @@ -0,0 +1,78 @@ +#include "vban.h" +#include "PulseAudioCPP.h" +#include + +VBAN_Stream::VBAN_Stream(std::string name, VBAN_CODEC codec, VBAN_SAMPLERATE samplerate, VBAN_DATATYPE datatype, uint8_t noChannels) + :m_FrameCounter(0) + ,m_name(name) +{ + m_header.vban = VBAN_MARKER; + for (int i = 0; i < 16; i++) + m_header.streamname[i] = m_name.size() > i ? m_name[i] : 0; + + m_header.format_bit = datatype & 0xf; + m_header.format_nbc = noChannels-1; + m_header.format_SR = samplerate; + m_header.format_nbs = 0; + m_header.noFrame = 0; + +} + +VBAN_Stream::~VBAN_Stream() +{ +} + +int VBAN_Stream::encodePacket(char* packet, uint16_t packetSize) +{ + //std::cout << "Encode Packet " << packetSize << std::endl; + auto total = packetSize + sizeof(VBAN_HEADER); + auto newPacket = new char[total]; + + std::cout << (void*)newPacket << std::endl; + + m_header.format_nbs = (packetSize / 8)-1; + m_header.noFrame = m_FrameCounter; + + for (int i = 0; i < sizeof(m_header); i++) + newPacket[i] = ((char*)&m_header)[i]; + + for (int i = 0; i < packetSize; i++) + newPacket[i + sizeof(VBAN_HEADER)] = packet[i]; + + + m_FrameCounter++; + m_packetBuffer.push_back({ newPacket,total }); + + //m_packetReadyCB(this, newPacket, packetSize); + return 0; +} + +int VBAN_Stream::decodePacket(char* packet, uint16_t packetSize) +{ + return 0; +} + +std::pair* VBAN_Stream::getLastPacket() +{ + if (m_packetBuffer.size() == 0) + return NULL; + return &m_packetBuffer.front(); +} + +void VBAN_Stream::dropLastPacket() +{ + if (m_packetBuffer.size() > 0) + { + //auto pack = m_packetBuffer.front(); + m_packetBuffer.pop_front(); + //delete[] pack.first; + } + +} + +void VBAN_Stream::update(Subject* s) +{ + PulseAudioCPP* pulseStream = (PulseAudioCPP*)s; + + encodePacket(pulseStream->getBuffer(), pulseStream->getBufferSize()); +} diff --git a/PulseAudio/3rd/vban.h b/PulseAudio/3rd/vban.h new file mode 100644 index 0000000..e9f597f --- /dev/null +++ b/PulseAudio/3rd/vban.h @@ -0,0 +1,174 @@ +#pragma once + +#include +#include "Observer.h" +#include "Subject.h" + +#define MTU_SIZE 1500 +#define VBAN_MAX_PACKET MTU_SIZE - sizeof(VBAN_HEADER) -28 + + +typedef unsigned int uint32_t ; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; + +#define VBAN_MARKER 0x4e414256 + + +#pragma pack(1) +struct VBAN_HEADER +{ + uint32_t vban; // contains 'V' 'B', 'A', 'N' + uint8_t format_SR; // SR index (see SRList above) + uint8_t format_nbs; // nb sample per frame (1 to 256) + uint8_t format_nbc; // nb channel (1 to 256) + uint8_t format_bit; // mask = 0x07 (see DATATYPE table below) + char streamname[16]; // stream name + uint32_t noFrame; // growing frame number +}; +#pragma pack() + +#define VBAN_CODEC_PCM 0x00 +#define VBAN_CODEC_VBCA 0x10 //VB-AUDIO AOIP CODEC +#define VBAN_CODEC_VBCV 0x20 //VB-AUDIO VOIP CODEC +#define VBAN_CODEC_UNDEFINED_1 0x30 +#define VBAN_CODEC_UNDEFINED_2 0x40 +#define VBAN_CODEC_UNDEFINED_3 0x50 +#define VBAN_CODEC_UNDEFINED_4 0x60 +#define VBAN_CODEC_UNDEFINED_5 0x70 +#define VBAN_CODEC_UNDEFINED_6 0x80 +#define VBAN_CODEC_UNDEFINED_7 0x90 +#define VBAN_CODEC_UNDEFINED_8 0xA0 +#define VBAN_CODEC_UNDEFINED_9 0xB0 +#define VBAN_CODEC_UNDEFINED_10 0xC0 +#define VBAN_CODEC_UNDEFINED_11 0xD0 +#define VBAN_CODEC_UNDEFINED_12 0xE0 +#define VBAN_CODEC_USER 0xF0 + +enum VBAN_CODEC +{ + CODEC_PCM = 0x00, + CODEC_VBCA = 0x10, + CODEC_VBCV = 0x20, + CODEC_UNDEF1 = 0x30, + CODEC_UNDEF2 = 0x40, + CODEC_UNDEF3 = 0x50, + CODEC_UNDEF4 = 0x60, + CODEC_UNDEF5 = 0x70, + CODEC_UNDEF6 = 0x80, + CODEC_UNDEF7 = 0x90, + CODEC_UNDEF8 = 0xA0, + CODEC_UNDEF9 = 0xB0, + CODEC_UNDEF10 = 0xC0, + CODEC_UNDEF11 = 0xD0, + CODEC_UNDEF12 = 0xE0, + CODEC_USER = 0xF0 + +}; + +#define VBAN_DATATYPE_BYTE8 0x00 +#define VBAN_DATATYPE_INT16 0x01 +#define VBAN_DATATYPE_INT24 0x02 +#define VBAN_DATATYPE_INT32 0x03 +#define VBAN_DATATYPE_FLOAT32 0x04 +#define VBAN_DATATYPE_FLOAT64 0x05 +#define VBAN_DATATYPE_12BITS 0x06 +#define VBAN_DATATYPE_10BITS 0x07 + +enum VBAN_DATATYPE +{ + DATATYPE_BYTE8 = 0x00, + DATATYPE_INT16 = 0x01, + DATATYPE_INT24 = 0x02, + DATATYPE_INT32 = 0x03, + DATATYPE_FLOAT32= 0x04, + DATATYPE_FLOAT64= 0x05, + DATATYPE_12BITS =0x06, + DATATYPE_10BITS =0x07 +}; + +#define VBAN_PROTOCOL_AUDIO 0x00 +#define VBAN_PROTOCOL_SERIAL 0x20 +#define VBAN_PROTOCOL_TXT 0x40 +#define VBAN_PROTOCOL_SERVICE 0x60 +#define VBAN_PROTOCOL_UNDEFINED_1 0x80 +#define VBAN_PROTOCOL_UNDEFINED_2 0xA0 +#define VBAN_PROTOCOL_UNDEFINED_3 0xC0 +#define VBAN_PROTOCOL_USER 0xE0 + +enum VBAN_PROTOCOL +{ + PROTOCOL_AUDIO = 0x00, + PROTOCOL_SERIAL = 0x20, + PROTOCOL_TXT = 0x40, + PROTOCOL_SERVICE = 0x60, + PROTOCOL_UNDEFINED_1 = 0x80, + PROTOCOL_UNDEFINED_2 = 0xA0, + PROTOCOL_UNDEFINED_3 = 0xC0, + PROTOCOL_USER = 0xE0 +}; + + +#define VBAN_SR_MAXNUMBER 21 +static long VBAN_SRList[VBAN_SR_MAXNUMBER] = +{ 6000, 12000, 24000, 48000, 96000, 192000, 384000, +8000, 16000, 32000, 64000, 128000, 256000, 512000, +11025, 22050, 44100, 88200, 176400, 352800, 705600 }; + +enum VBAN_SAMPLERATE +{ + SR_6000, + SR_12000, + SR_24000, + SR_48000, + SR_96000, + SR_192000, + SR_384000, + SR_8000, + SR_16000, + SR_32000, + SR_64000, + SR_128000, + SR_256000, + SR_512000, + SR_11025, + SR_22050, + SR_44100, + SR_88200, + SR_176400, + SR_352800, + SR_705600 +}; + +class VBAN_Stream; + +typedef int (*VBAN_StreamSamplesReady)(VBAN_Stream* stream, char* samples, int noSamples); + +class VBAN_Stream :public Observer +{ +public: + VBAN_Stream(std::string name, VBAN_CODEC codec, VBAN_SAMPLERATE samplerate, VBAN_DATATYPE datatype, uint8_t noChannels); + virtual ~VBAN_Stream(); + + + int encodePacket(char * packet, uint16_t packetSize); + int decodePacket(char* packet, uint16_t packetSize); + + std::pair* getLastPacket(); + void dropLastPacket(); + + void update(Subject* s); +private: + std::string m_name; + VBAN_CODEC m_codec; + VBAN_SAMPLERATE m_samplerate; + VBAN_DATATYPE m_datatype; + uint8_t m_noChannels; + + uint32_t m_FrameCounter; + + VBAN_StreamSamplesReady m_packetReadyCB; + + std::list> m_packetBuffer; + VBAN_HEADER m_header; +}; diff --git a/PulseAudio/PulseAudio.vcxproj b/PulseAudio/PulseAudio.vcxproj new file mode 100644 index 0000000..02b6a88 --- /dev/null +++ b/PulseAudio/PulseAudio.vcxproj @@ -0,0 +1,110 @@ + + + + + Debug + ARM + + + Release + ARM + + + Debug + ARM64 + + + Release + ARM64 + + + Debug + x86 + + + Release + x86 + + + Debug + x64 + + + Release + x64 + + + + {567419ea-59c6-4393-bbd3-d3850887af8b} + Linux + PulseAudio + 15.0 + Linux + 1.0 + Generic + {2238F9CD-F817-4ECC-BD14-2524D2669B35} + + + + true + + + false + + + true + + + false + + + true + + + false + + + false + + + true + + + + + + + + ./inc;./ext + + + ./inc;./3rd + + + + + + + + + + + + + + + + + + + pulse + + + + + pulse + + + + + \ No newline at end of file diff --git a/PulseAudio/inc/PulseAudioCPP.h b/PulseAudio/inc/PulseAudioCPP.h new file mode 100644 index 0000000..6b2e731 --- /dev/null +++ b/PulseAudio/inc/PulseAudioCPP.h @@ -0,0 +1,43 @@ +#pragma once +#include +#include +#include + +#include "../3rd/Subject.h" + +class PulseAudioCPP : public Subject +{ +public: + PulseAudioCPP(std::string appName); + virtual ~PulseAudioCPP(); + + void createStream(std::string name); + + char* getBuffer() { return mp_buffer; }; + unsigned int getBufferSize() { return m_bytesInBuffer; }; + +private: + static void subscriptionCB(pa_context* c, pa_subscription_event_type_t t, uint32_t idx, void* userdata); + static void contextCB(pa_context* c, int success, void* userdata); + static void contextReadyCB(pa_context* context, void* userdata); + + //stream callback + void readStream(); + + static void streamSuccessCB (pa_stream* s, int success, void* userdata); + static void streamRequestCB (pa_stream* p, size_t nbytes, void* userdata); + static void streamNotifyCB (pa_stream* p, void* userdata); + static void streamEventCB (pa_stream* p, const char* name, pa_proplist* pl, void* userdata); + + +private: + pa_threaded_mainloop* mp_threadedLoop; + pa_mainloop_api* mp_mainLoop; + pa_context* mp_context; + + std::list mp_streamList; + + char* mp_buffer; + unsigned int m_bytesInBuffer; +}; + diff --git a/PulseAudio/inc/PulseAudioStream.h b/PulseAudio/inc/PulseAudioStream.h new file mode 100644 index 0000000..749f4ad --- /dev/null +++ b/PulseAudio/inc/PulseAudioStream.h @@ -0,0 +1,19 @@ +#pragma once +#include "PulseAudioCPP.h" +#include "../3rd/Subject.h" + +class PulseAudioStream : public PulseAudioCPP +{ +public: + PulseAudioStream(std::string name); + virtual ~PulseAudioStream(); + + + + int setSamplesReadyCallback(); + + +private: + +}; + diff --git a/PulseAudio/inc/pulseaudioEnumPrint.h b/PulseAudio/inc/pulseaudioEnumPrint.h new file mode 100644 index 0000000..527d38f --- /dev/null +++ b/PulseAudio/inc/pulseaudioEnumPrint.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include + +std::ostream& operator<<(std::ostream& out, const pa_subscription_mask value) { + return out << [value] { +#define PROCESS_VAL(p) case(p): return #p; + switch (value) { + PROCESS_VAL(PA_SUBSCRIPTION_MASK_NULL); + PROCESS_VAL(PA_SUBSCRIPTION_MASK_SINK); + PROCESS_VAL(PA_SUBSCRIPTION_MASK_SOURCE); + PROCESS_VAL(PA_SUBSCRIPTION_MASK_SINK_INPUT); + PROCESS_VAL(PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT); + PROCESS_VAL(PA_SUBSCRIPTION_MASK_MODULE); + PROCESS_VAL(PA_SUBSCRIPTION_MASK_CLIENT); + PROCESS_VAL(PA_SUBSCRIPTION_MASK_SAMPLE_CACHE); + PROCESS_VAL(PA_SUBSCRIPTION_MASK_SERVER); + PROCESS_VAL(PA_SUBSCRIPTION_MASK_AUTOLOAD); + PROCESS_VAL(PA_SUBSCRIPTION_MASK_CARD); + PROCESS_VAL(PA_SUBSCRIPTION_MASK_ALL); + default: + return "Unkown Enum value"; + } +#undef PROCESS_VAL + }(); +} + +std::ostream& operator<<(std::ostream& out, const pa_subscription_event_type value) { + return out << [value] { +#define PROCESS_VAL(p) case(p): return #p; + switch (value) { + PROCESS_VAL(PA_SUBSCRIPTION_EVENT_SOURCE); + PROCESS_VAL(PA_SUBSCRIPTION_EVENT_SINK_INPUT); + PROCESS_VAL(PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT); + PROCESS_VAL(PA_SUBSCRIPTION_EVENT_MODULE); + PROCESS_VAL(PA_SUBSCRIPTION_EVENT_CLIENT); + PROCESS_VAL(PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE); + PROCESS_VAL(PA_SUBSCRIPTION_EVENT_SERVER); + PROCESS_VAL(PA_SUBSCRIPTION_EVENT_AUTOLOAD); + PROCESS_VAL(PA_SUBSCRIPTION_EVENT_CARD); + PROCESS_VAL(PA_SUBSCRIPTION_EVENT_FACILITY_MASK); + PROCESS_VAL(PA_SUBSCRIPTION_EVENT_CHANGE); + PROCESS_VAL(PA_SUBSCRIPTION_EVENT_REMOVE); + PROCESS_VAL(PA_SUBSCRIPTION_EVENT_TYPE_MASK); + case PA_SUBSCRIPTION_EVENT_NEW: + return "PA_SUBSCRIPTION_EVENT_NEW|PA_SUBSCRIPTION_EVENT_SINK"; + default: + return "Unkown Enum value"; + } +#undef PROCESS_VAL + }(); +} \ No newline at end of file diff --git a/PulseAudio/src/PulseAudioCPP.cpp b/PulseAudio/src/PulseAudioCPP.cpp new file mode 100644 index 0000000..c9bb731 --- /dev/null +++ b/PulseAudio/src/PulseAudioCPP.cpp @@ -0,0 +1,152 @@ +#include "PulseAudioCPP.h" +#include +#include "pulseaudioEnumPrint.h" +#include + +PulseAudioCPP::PulseAudioCPP(std::string appName) + :mp_context(NULL) + , mp_mainLoop(NULL) + , mp_threadedLoop(NULL) + ,mp_buffer(new char[4096]) +{ + + mp_threadedLoop = pa_threaded_mainloop_new(); + mp_mainLoop = pa_threaded_mainloop_get_api(mp_threadedLoop); + mp_context = pa_context_new(mp_mainLoop, "VBAN Audio Server"); + + pa_context_set_state_callback(mp_context, PulseAudioCPP::contextReadyCB, this); + + + + pa_threaded_mainloop_lock(mp_threadedLoop); + pa_threaded_mainloop_start(mp_threadedLoop); + + pa_context_connect(mp_context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL); + + while (1) + { + pa_context_state_t context_state = pa_context_get_state(mp_context); + if (context_state == PA_CONTEXT_READY) break; + pa_threaded_mainloop_wait(mp_threadedLoop); + } + + pa_context_subscribe(mp_context, PA_SUBSCRIPTION_MASK_ALL, PulseAudioCPP::contextCB, this); + pa_context_set_subscribe_callback(mp_context, PulseAudioCPP::subscriptionCB, this); + + +} + +PulseAudioCPP::~PulseAudioCPP() +{ + if (mp_context) + pa_context_disconnect(mp_context); +} + +void PulseAudioCPP::createStream(std::string name) +{ + pa_sample_spec spec; + spec.channels = 2; + spec.format = pa_sample_format_t::PA_SAMPLE_FLOAT32LE; + spec.rate = 44100; + + pa_channel_map map; + map.channels = 2; + map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; + map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; + + pa_stream* newStream = pa_stream_new(mp_context, name.c_str(), &spec, &map); + + pa_buffer_attr buff; + buff.fragsize = 1400; //VBAN Packet size + buff.maxlength = (uint32_t)-1; + buff.minreq = (uint32_t)-1; + buff.prebuf = (uint32_t)-1; + buff.tlength = (uint32_t)-1; + + pa_stream_set_state_callback(newStream, PulseAudioCPP::streamNotifyCB, this); + pa_stream_set_read_callback(newStream, PulseAudioCPP::streamRequestCB, this); + + pa_stream_connect_record(newStream, NULL, &buff, pa_stream_flags_t::PA_STREAM_NOFLAGS); + + // Wait for the stream to be ready + for (;;) { + pa_stream_state_t stream_state = pa_stream_get_state(newStream); + if (stream_state == PA_STREAM_READY) break; + pa_threaded_mainloop_wait(mp_threadedLoop); + } + + pa_threaded_mainloop_unlock(mp_threadedLoop); + + mp_streamList.push_back(newStream); +} + +void PulseAudioCPP::subscriptionCB(pa_context* c, pa_subscription_event_type_t t, uint32_t idx, void* instance) +{ + auto ins = (PulseAudioCPP*)instance; + + auto t1 = pa_subscription_event_type_t(t & 0x0f); + auto t2 = pa_subscription_event_type_t(t & 0xf0); + std::cout << "Subscription callback: " << idx << " " << t1 << " " << t2 << std::endl; + + + if (t2 == pa_subscription_event_type_t::PA_SUBSCRIPTION_EVENT_NEW && t1 == pa_subscription_event_type_t::PA_SUBSCRIPTION_EVENT_SINK_INPUT) + { + + } +} + +void PulseAudioCPP::contextCB(pa_context* c, int success, void* instance) +{ + auto ins = (PulseAudioCPP*)instance; + + std::cout << "Success: " << success << std::endl; +} + +void PulseAudioCPP::contextReadyCB(pa_context* context, void* instance) +{ + auto ins = (PulseAudioCPP*)instance; + + std::cout << "Context state callback!" << std::endl; + pa_threaded_mainloop_signal((pa_threaded_mainloop*)ins->mp_threadedLoop, 0); +} + +void PulseAudioCPP::readStream() +{ + char* bufferp; + size_t size = 0; + auto s = mp_streamList.front(); + if (s == NULL) + return; + pa_stream_peek(s, (const void**)&bufferp, &size); + + m_bytesInBuffer = size; + for (int i = 0; i < size; i++) + mp_buffer[i] = bufferp[i]; + + //std::cout << "Samples: " << size / 8 << std::endl; + this->notify(this); + + pa_stream_drop(s); +} + +void PulseAudioCPP::streamSuccessCB(pa_stream* s, int success, void* userdata) +{ +} + +void PulseAudioCPP::streamRequestCB(pa_stream* p, size_t nbytes, void* userdata) +{ + auto instance = (PulseAudioCPP*)userdata; + instance->readStream(); +} + +void PulseAudioCPP::streamNotifyCB(pa_stream* p, void* userdata) +{ + auto ins = (PulseAudioCPP*)userdata; + + std::cout << "Stream Notify callback!" << std::endl; + pa_threaded_mainloop_signal((pa_threaded_mainloop*)ins->mp_threadedLoop, 0); +} + +void PulseAudioCPP::streamEventCB(pa_stream* p, const char* name, pa_proplist* pl, void* userdata) +{ +} diff --git a/PulseAudio/src/PulseAudioStream.cpp b/PulseAudio/src/PulseAudioStream.cpp new file mode 100644 index 0000000..7a67b88 --- /dev/null +++ b/PulseAudio/src/PulseAudioStream.cpp @@ -0,0 +1 @@ +#include "PulseAudioStream.h" diff --git a/PulseAudio/src/main.cpp b/PulseAudio/src/main.cpp new file mode 100644 index 0000000..9fbceed --- /dev/null +++ b/PulseAudio/src/main.cpp @@ -0,0 +1,55 @@ +#include +#include + +#include "PulseAudioCPP.h" +#include "../3rd/vban.h" +#include +#include + +class udpStream +{ +public: + udpStream(std::string target) + { + m_socket = socket(AF_INET, SOCK_DGRAM, 0); + for (int i = 0; i < sizeof(m_addr); i++) + ((char*)&m_addr)[i] = 0; + m_addr.sin_family = AF_INET; + m_addr.sin_port = htons(6980); + inet_aton(target.c_str(), &m_addr.sin_addr); + } + + virtual ~udpStream() {}; + + int send(const char* data, unsigned short size) + { + sendto(m_socket, data, size, 0, (struct sockaddr*)&m_addr, sizeof(m_addr)); + } +private: + int m_socket; + struct sockaddr_in m_addr; +}; + +int main() +{ + PulseAudioCPP vbanServer("vban server"); + + vbanServer.createStream("testStream"); + VBAN_Stream stream("Stream1", VBAN_CODEC::CODEC_PCM, VBAN_SAMPLERATE::SR_44100, VBAN_DATATYPE::DATATYPE_FLOAT32, 2); + + vbanServer.addObserver(&stream); + udpStream udp("192.168.10.19"); + + + while (1) + { + auto packet = stream.getLastPacket(); + if (packet != NULL) + { + udp.send(packet->first, packet->second); + stream.dropLastPacket(); + } + } + + return 0; +} \ No newline at end of file diff --git a/inc/vban-protocol.h b/inc/vban-protocol.h new file mode 100644 index 0000000..3fa5047 --- /dev/null +++ b/inc/vban-protocol.h @@ -0,0 +1,8 @@ +// vban-protocol.h : Include file for standard system include files, +// or project specific include files. + +#pragma once + +#include + +// TODO: Reference additional headers your program requires here. diff --git a/inc/vban.h b/inc/vban.h new file mode 100644 index 0000000..d2d7570 --- /dev/null +++ b/inc/vban.h @@ -0,0 +1,179 @@ +#pragma once + +#define WINDOWS 1 + +#ifdef WINDOWS +#include +#endif // WINDOWS + + +#define MTU_SIZE 1500 +#define VBAN_MAX_PACKET MTU_SIZE - sizeof(VBAN_HEADER) -28 + +typedef long long uint32_t ; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; + +#pragma pack(1) +struct VBAN_HEADER +{ + uint32_t vban; // contains 'V' 'B', 'A', 'N' + uint8_t format_SR; // SR index (see SRList above) + uint8_t format_nbs; // nb sample per frame (1 to 256) + uint8_t format_nbc; // nb channel (1 to 256) + uint8_t format_bit; // mask = 0x07 (see DATATYPE table below) + char streamname[16]; // stream name + uint32_t nuFrame; // growing frame number +}; +#pragma pack() + +#define VBAN_CODEC_PCM 0x00 +#define VBAN_CODEC_VBCA 0x10 //VB-AUDIO AOIP CODEC +#define VBAN_CODEC_VBCV 0x20 //VB-AUDIO VOIP CODEC +#define VBAN_CODEC_UNDEFINED_1 0x30 +#define VBAN_CODEC_UNDEFINED_2 0x40 +#define VBAN_CODEC_UNDEFINED_3 0x50 +#define VBAN_CODEC_UNDEFINED_4 0x60 +#define VBAN_CODEC_UNDEFINED_5 0x70 +#define VBAN_CODEC_UNDEFINED_6 0x80 +#define VBAN_CODEC_UNDEFINED_7 0x90 +#define VBAN_CODEC_UNDEFINED_8 0xA0 +#define VBAN_CODEC_UNDEFINED_9 0xB0 +#define VBAN_CODEC_UNDEFINED_10 0xC0 +#define VBAN_CODEC_UNDEFINED_11 0xD0 +#define VBAN_CODEC_UNDEFINED_12 0xE0 +#define VBAN_CODEC_USER 0xF0 + +enum VBAN_CODEC +{ + CODEC_PCM = 0x00, + CODEC_VBCA = 0x10, + CODEC_VBCV = 0x20, + CODEC_UNDEF1 = 0x30, + CODEC_UNDEF2 = 0x40, + CODEC_UNDEF3 = 0x50, + CODEC_UNDEF4 = 0x60, + CODEC_UNDEF5 = 0x70, + CODEC_UNDEF6 = 0x80, + CODEC_UNDEF7 = 0x90, + CODEC_UNDEF8 = 0xA0, + CODEC_UNDEF9 = 0xB0, + CODEC_UNDEF10 = 0xC0, + CODEC_UNDEF11 = 0xD0, + CODEC_UNDEF12 = 0xE0, + CODEC_USER = 0xF0 + +}; + +#define VBAN_DATATYPE_BYTE8 0x00 +#define VBAN_DATATYPE_INT16 0x01 +#define VBAN_DATATYPE_INT24 0x02 +#define VBAN_DATATYPE_INT32 0x03 +#define VBAN_DATATYPE_FLOAT32 0x04 +#define VBAN_DATATYPE_FLOAT64 0x05 +#define VBAN_DATATYPE_12BITS 0x06 +#define VBAN_DATATYPE_10BITS 0x07 + +enum VBAN_DATATYPE +{ + DATATYPE_BYTE8 = 0x00, + DATATYPE_INT16 = 0x01, + DATATYPE_INT24 = 0x02, + DATATYPE_INT32 = 0x03, + DATATYPE_FLOAT32= 0x04, + DATATYPE_FLOAT64= 0x05, + DATATYPE_12BITS =0x06, + DATATYPE_10BITS =0x07 +}; + +#define VBAN_PROTOCOL_AUDIO 0x00 +#define VBAN_PROTOCOL_SERIAL 0x20 +#define VBAN_PROTOCOL_TXT 0x40 +#define VBAN_PROTOCOL_SERVICE 0x60 +#define VBAN_PROTOCOL_UNDEFINED_1 0x80 +#define VBAN_PROTOCOL_UNDEFINED_2 0xA0 +#define VBAN_PROTOCOL_UNDEFINED_3 0xC0 +#define VBAN_PROTOCOL_USER 0xE0 + +enum VBAN_PROTOCOL +{ + PROTOCOL_AUDIO = 0x00, + PROTOCOL_SERIAL = 0x20, + PROTOCOL_TXT = 0x40, + PROTOCOL_SERVICE = 0x60, + PROTOCOL_UNDEFINED_1 = 0x80, + PROTOCOL_UNDEFINED_2 = 0xA0, + PROTOCOL_UNDEFINED_3 = 0xC0, + PROTOCOL_USER = 0xE0 +}; + + +#define VBAN_SR_MAXNUMBER 21 +static long VBAN_SRList[VBAN_SR_MAXNUMBER] = +{ 6000, 12000, 24000, 48000, 96000, 192000, 384000, +8000, 16000, 32000, 64000, 128000, 256000, 512000, +11025, 22050, 44100, 88200, 176400, 352800, 705600 }; + +enum VBAN_SAMPLERATE +{ + SR_6000, + SR_12000, + SR_24000, + SR_48000, + SR_96000, + SR_192000, + SR_384000, + SR_8000, + SR_16000, + SR_32000, + SR_64000, + SR_128000, + SR_256000, + SR_512000, + SR_11025, + SR_22050, + SR_44100, + SR_88200, + SR_176400, + SR_352800, + SR_705600 +}; + +typedef int VBAN_StreamSamplesReady(VBAN_STREAM* stream, char* samples, int noSamples); + +typedef struct VBAN_handle +{ + VBAN_STREAM* streams; + uint8_t noStreams; + + VBAN_StreamSamplesReady* readyCallback; +} VBAN_handle; + + +typedef struct VBAN_STREAM +{ + char name[16]; + VBAN_CODEC codec; + VBAN_SAMPLERATE samplerate; + VBAN_DATATYPE datatype; + uint8_t noChannels; + + uint32_t frameCounter; + + char* buffer; + uint16_t bufferSize; +} VBAN_STREAM; + + + + +//Returns -1 for error +int VBAN_Init(VBAN_handle*, char * buffer, uint16_t bufferSize); + +int VBAN_GetCodec(); + +//Returns the size of the byte in the packet, -1 means error +int VBAN_Encode(VBAN_handle *handle, char *packet, uint16_t packetSize, char* audioStream, uint16_t noSamples); + +//Returns the number of samples decoded from the packet, -1 means error +int VBAN_Decode(VBAN_handle* handle, char* packet, uint16_t packetSize, char* audioStream, uint16_t noSamples); \ No newline at end of file diff --git a/src/vban-protocol.cpp b/src/vban-protocol.cpp new file mode 100644 index 0000000..246594b --- /dev/null +++ b/src/vban-protocol.cpp @@ -0,0 +1,12 @@ +// vban-protocol.cpp : Defines the entry point for the application. +// + +#include "vban-protocol.h" + +using namespace std; + +int main() +{ + cout << "Hello CMake." << endl; + return 0; +} diff --git a/src/vban.c b/src/vban.c new file mode 100644 index 0000000..495e973 --- /dev/null +++ b/src/vban.c @@ -0,0 +1,17 @@ +#include "vban.h" + +int VBAN_Init(VBAN_handle* h, char* buffer, uint16_t bufferSize) +{ + + return 0; +} + +unsigned int VBAN_Encode(VBAN_handle* handle, char* packet, uint16_t packetSize, char* audioStream, uint16_t noSamples) +{ + return 0; +} + +unsigned int VBAN_Decode(VBAN_handle* handle, char* packet, uint16_t packetSize, char* audioStream, uint16_t noSamples) +{ + return 0; +}