Guest User

Untitled

a guest
Jun 3rd, 2025
207
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 10.07 KB | None | 0 0
  1. #include <pipewire/pipewire.h>
  2. #include <vector>
  3. #include <string>
  4. #include <iostream>
  5. #include <cmath> // For std::sin, std::cos, etc. if needed for processing
  6.  
  7. // Forward declaration for the PipewireProcessor class
  8. class PipewireProcessor;
  9.  
  10. /**
  11.  * @brief Configuration options for the PipewireProcessor.
  12.  */
  13. struct PipewireProcessorConfig {
  14.     std::string ClientName;
  15.     std::string NodeName;
  16.     pw_direction_t PortDirection; // e.g., PW_DIRECTION_INPUT, PW_DIRECTION_OUTPUT
  17.     int SampleRate;
  18.     int Channels;
  19.     uint32_t BufferSize; // In frames
  20. };
  21.  
  22. /**
  23.  * @brief C++ class to encapsulate PipeWire audio processing.
  24.  */
  25. class PipewireProcessor {
  26. public:
  27.     PipewireProcessor();
  28.     ~PipewireProcessor();
  29.  
  30.     bool Initialize(const PipewireProcessorConfig& config);
  31.     void Run();
  32.     void Stop();
  33.  
  34. private:
  35.     // PipeWire related members
  36.     pw_loop* MainLoop;
  37.     pw_context* Context;
  38.     pw_core* Core;
  39.     pw_filter* Filter;
  40.     PipewireProcessorConfig Config;
  41.  
  42.     // Buffers for audio data
  43.     std::vector<float> InputBuffer;
  44.     std::vector<float> OutputBuffer;
  45.  
  46.     // Static callback function for PipeWire process events
  47.     static void ProcessCallback(void* userdata, struct pw_buffer* buffer);
  48.  
  49.     // Member function to handle audio processing
  50.     void ProcessAudio(struct pw_buffer* buffer);
  51.  
  52.     // Private helper for error handling
  53.     void HandleError(const std::string& message);
  54. };
  55.  
  56. // --- Implementation ---
  57.  
  58. PipewireProcessor::PipewireProcessor()
  59.     : MainLoop(nullptr), Context(nullptr), Core(nullptr), Filter(nullptr) {
  60.     pw_init(nullptr, nullptr);
  61. }
  62.  
  63. PipewireProcessor::~PipewireProcessor() {
  64.     Stop(); // Ensure all PipeWire resources are released
  65.     if (Context) {
  66.         pw_context_destroy(Context);
  67.     }
  68.     if (MainLoop) {
  69.         pw_loop_destroy(MainLoop);
  70.     }
  71.     pw_deinit();
  72. }
  73.  
  74. bool PipewireProcessor::Initialize(const PipewireProcessorConfig& config) {
  75.     Config = config;
  76.  
  77.     MainLoop = pw_loop_new(nullptr);
  78.     if (!MainLoop) {
  79.         HandleError("Failed to create PipeWire main loop.");
  80.         return false;
  81.     }
  82.  
  83.     Context = pw_context_new(MainLoop, nullptr);
  84.     if (!Context) {
  85.         HandleError("Failed to create PipeWire context.");
  86.         return false;
  87.     }
  88.  
  89.     Core = pw_context_connect(Context, nullptr, 0);
  90.     if (!Core) {
  91.         HandleError("Failed to connect to PipeWire core.");
  92.         return false;
  93.     }
  94.  
  95.     // Configure the filter
  96.     std::string node_name = Config.NodeName;
  97.     std::string client_name = Config.ClientName;
  98.  
  99.     const char* filter_properties_str =
  100.         "node.name = \"" + node_name + "\"\n"
  101.         "client.name = \"" + client_name + "\"";
  102.  
  103.     pw_properties* props = pw_properties_new_string(filter_properties_str);
  104.     if (!props) {
  105.         HandleError("Failed to create filter properties.");
  106.         return false;
  107.     }
  108.  
  109.     pw_filter_callbacks filter_cbs = {
  110.         .version = PW_VERSION_FILTER_CALLBACKS,
  111.         .process = ProcessCallback,
  112.     };
  113.  
  114.     Filter = pw_filter_new(Core, PW_FILTER_FLAG_RT_PROCESS, props,
  115.                            PW_KEY_NODE_NAME, node_name.c_str(),
  116.                            nullptr); // Additional properties can be added here
  117.     if (!Filter) {
  118.         HandleError("Failed to create PipeWire filter.");
  119.         pw_properties_free(props);
  120.         return false;
  121.     }
  122.  
  123.     // Bind this object instance to the userdata of the filter
  124.     pw_filter_set_userdata(Filter, this);
  125.     pw_filter_set_callbacks(Filter, &filter_cbs);
  126.  
  127.     // Configure ports
  128.     pw_port_info port_info;
  129.     port_info.direction = Config.PortDirection;
  130.     port_info.n_buffers = 0; // Let PipeWire decide
  131.     port_info.flags = PW_PORT_FLAG_MAP_BUFFERS;
  132.  
  133.     pw_properties* port_props = pw_properties_new(nullptr, nullptr);
  134.     if (!port_props) {
  135.         HandleError("Failed to create port properties.");
  136.         return false;
  137.     }
  138.     pw_properties_setf(port_props, PW_KEY_MEDIA_TYPE, "Audio");
  139.     pw_properties_setf(port_props, PW_KEY_MEDIA_SUBTYPE, "Raw");
  140.     pw_properties_setf(port_props, PW_KEY_AUDIO_FORMAT, "F32"); // Float 32-bit
  141.     pw_properties_setf(port_props, PW_KEY_AUDIO_RATE, "%d", Config.SampleRate);
  142.     pw_properties_setf(port_props, PW_KEY_AUDIO_CHANNELS, "%d", Config.Channels);
  143.     pw_properties_setf(port_props, PW_KEY_NODE_LATENCY, "%d/%d", Config.BufferSize, Config.SampleRate);
  144.  
  145.  
  146.     if (pw_filter_add_port(Filter, Config.PortDirection, PW_FILTER_PORT_FLAG_MAP_BUFFERS, port_props) == nullptr) {
  147.         HandleError("Failed to add port to filter.");
  148.         pw_properties_free(port_props);
  149.         return false;
  150.     }
  151.  
  152.     InputBuffer.resize(Config.BufferSize * Config.Channels);
  153.     OutputBuffer.resize(Config.BufferSize * Config.Channels);
  154.  
  155.     if (pw_filter_activate(Filter, PW_FILTER_FLAG_RT_PROCESS, 0, nullptr) < 0) {
  156.         HandleError("Failed to activate filter.");
  157.         return false;
  158.     }
  159.  
  160.     return true;
  161. }
  162.  
  163. void PipewireProcessor::Run() {
  164.     if (MainLoop) {
  165.         std::cout << "PipeWire audio loop running..." << std::endl;
  166.         pw_loop_run(MainLoop);
  167.     }
  168. }
  169.  
  170. void PipewireProcessor::Stop() {
  171.     if (Filter) {
  172.         pw_filter_deactivate(Filter);
  173.         pw_filter_destroy(Filter);
  174.         Filter = nullptr;
  175.     }
  176.     if (Core) {
  177.         pw_core_disconnect(Core);
  178.         Core = nullptr;
  179.     }
  180.     if (MainLoop) {
  181.         pw_loop_quit(MainLoop);
  182.     }
  183. }
  184.  
  185. void PipewireProcessor::ProcessCallback(void* userdata, struct pw_buffer* buffer) {
  186.     PipewireProcessor* self = static_cast<PipewireProcessor*>(userdata);
  187.     self->ProcessAudio(buffer);
  188. }
  189.  
  190. void PipewireProcessor::ProcessAudio(struct pw_buffer* buffer) {
  191.     if (!buffer || buffer->n_datas == 0) {
  192.         std::cerr << "Warning: Received empty buffer." << std::endl;
  193.         return;
  194.     }
  195.  
  196.     struct pw_data* input_data = &buffer->datas[0]; // Assuming one input port
  197.     struct pw_data* output_data = &buffer->datas[0]; // Assuming one output port (in-place processing)
  198.  
  199.     // Check if it's an output-only port or input-only port scenario
  200.     if (Config.PortDirection == PW_DIRECTION_OUTPUT) {
  201.         // Output-only port: write data to the output buffer
  202.         float* out = static_cast<float*>(output_data->data);
  203.         size_t n_frames = output_data->maxsize / (Config.Channels * sizeof(float));
  204.  
  205.         // Ensure buffer size is not exceeded
  206.         n_frames = std::min(n_frames, static_cast<size_t>(Config.BufferSize));
  207.         output_data->chunksize = n_frames * Config.Channels * sizeof(float);
  208.  
  209.         // Example processing: generate a sine wave
  210.         static double phase = 0.0;
  211.         double freq = 440.0; // Hz
  212.         double phase_increment = (2.0 * M_PI * freq) / Config.SampleRate;
  213.  
  214.         for (size_t i = 0; i < n_frames * Config.Channels; ++i) {
  215.             if (i % Config.Channels == 0) { // Only increment phase for the first channel of each frame
  216.                 out[i] = static_cast<float>(0.5 * std::sin(phase)); // Left channel
  217.                 if (Config.Channels > 1) {
  218.                     out[i+1] = static_cast<float>(0.5 * std::cos(phase)); // Right channel (example)
  219.                 }
  220.                 phase += phase_increment;
  221.                 if (phase >= 2.0 * M_PI) {
  222.                     phase -= 2.0 * M_PI;
  223.                 }
  224.             } else if (Config.Channels > 1 && i % Config.Channels == 1) {
  225.                 // Already handled in the i % Config.Channels == 0 case if processing stereo
  226.             } else {
  227.                 // For more than 2 channels, fill with silence or replicate
  228.                 out[i] = 0.0f;
  229.             }
  230.         }
  231.     } else if (Config.PortDirection == PW_DIRECTION_INPUT) {
  232.         // Input-only port: read data from the input buffer
  233.         const float* in = static_cast<const float*>(input_data->data);
  234.         size_t n_frames = input_data->chunksize / (Config.Channels * sizeof(float));
  235.  
  236.         // Process incoming audio data (e.g., print RMS, analyze, etc.)
  237.         double sum_sq = 0.0;
  238.         for (size_t i = 0; i < n_frames * Config.Channels; ++i) {
  239.             sum_sq += in[i] * in[i];
  240.         }
  241.         // double rms = std::sqrt(sum_sq / (n_frames * Config.Channels));
  242.         // std::cout << "RMS: " << rms << std::endl;
  243.     } else {
  244.         // Bi-directional port (input and output are effectively the same buffer)
  245.         // Read from input_data->data and write to output_data->data
  246.         // For in-place processing, you can directly modify input_data->data
  247.         float* audio_data = static_cast<float*>(input_data->data);
  248.         size_t n_frames = input_data->chunksize / (Config.Channels * sizeof(float));
  249.  
  250.         // Example: simple gain reduction
  251.         for (size_t i = 0; i < n_frames * Config.Channels; ++i) {
  252.             audio_data[i] *= 0.8f; // Reduce gain by 20%
  253.         }
  254.         output_data->chunksize = input_data->chunksize; // Output size matches input size
  255.     }
  256. }
  257.  
  258. void PipewireProcessor::HandleError(const std::string& message) {
  259.     std::cerr << "PipeWire Error: " << message << std::endl;
  260. }
  261.  
  262. // --- Example Usage ---
  263.  
  264. int main() {
  265.     PipewireProcessorConfig config;
  266.     config.ClientName = "MyPipewireClient";
  267.     config.NodeName = "MyAudioProcessor";
  268.     config.PortDirection = PW_DIRECTION_OUTPUT; // Or PW_DIRECTION_INPUT, or for in-place processing set up input and output ports
  269.     config.SampleRate = 48000;
  270.     config.Channels = 2; // Stereo
  271.     config.BufferSize = 1024; // Frames
  272.  
  273.     PipewireProcessor processor;
  274.     if (!processor.Initialize(config)) {
  275.         std::cerr << "Failed to initialize PipeWire processor." << std::endl;
  276.         return 1;
  277.     }
  278.  
  279.     processor.Run();
  280.  
  281.     // The Run() method is blocking. To stop it, you would typically use a signal handler
  282.     // or run it in a separate thread and call processor.Stop() from another thread.
  283.     // For this example, it will run indefinitely until manually terminated.
  284.  
  285.     // Example of how to stop after a delay (for testing purposes, not for production)
  286.     // #include <chrono>
  287.     // #include <thread>
  288.     // std::this_thread::sleep_for(std::chrono::seconds(10));
  289.     // processor.Stop();
  290.  
  291.     return 0;
  292. }
Advertisement
Add Comment
Please, Sign In to add comment