SOUND4 IMPACT.CL Library [1.2.3]
sound4.impact.cl-pipe.cpp
Go to the documentation of this file.
1 
4 #include <unistd.h>
5 #include <iostream>
6 #include <strings.h>
7 #include <sys/types.h>
8 #include <thread> // NOTE: needs pthread link
9 // Choose good filesystem version depending on compiler.
10 #if __has_include(<filesystem>)
11  #include <filesystem>
12 #else //if __cpp_lib_experimental_filesystem
13  // We need the alias from std::experimental::filesystem to std::filesystem
14  #include <experimental/filesystem>
15  namespace std {
16  namespace filesystem = experimental::filesystem;
17  }
18 #endif
19 #if defined(_WIN32) || defined(WIN32)
20  #include <io.h>
21  #include <fcntl.h>
22  #include <stringapiset.h> // UTF-8 conversion
23 #endif
24 #include <sound4.impact.cl.h>
25 
26 #define SAMPLE_RATE 48000
27 #define CHUNK_SIZE 64
28 #define CHANNEL_COUNT 2
29 
30 // Reads from stdin, write to stdout
31 void ProcessLoop(struct impact_CInstance *pInstance)
32 {
34 bool stopthread=false;
35 alignas(64) float audio_in[CHUNK_SIZE*CHANNEL_COUNT];
36 alignas(64) float audio_out[CHUNK_SIZE*CHANNEL_COUNT];
37 
38  #if defined(_WIN32) || defined(WIN32)
39  // Windows: The stdin, stdout, and stderr streams always open in text mode by default
40  _setmode(_fileno(stdin), _O_BINARY);
41  _setmode(_fileno(stdout), _O_BINARY);
42  #endif
43 
44  while (!stopthread)
45  {
46  // Read a chunk from stdin
47  if (std::fread(&audio_in[0], sizeof(float), CHUNK_SIZE*CHANNEL_COUNT, stdin) != CHUNK_SIZE*CHANNEL_COUNT) {
48  std::cerr << "ProcessLoop: input stopped"<< std::endl;
49  stopthread=true;
50  break;
51  }
52  if (stopthread) break;
53  // Process it
54  impact_ProcessAudio(pInstance, audio_in, audio_out);
55  // Write it to stdout
56  if (fwrite(&audio_out[0], sizeof(float), CHUNK_SIZE*CHANNEL_COUNT, stdout) != CHUNK_SIZE*CHANNEL_COUNT) {
57  std::cerr << "Failed to write audio output" << std::endl;
58  stopthread=true;
59  }
60  }
62 }
63 
64 void Usage(const char *progname)
65 {
66  std::cerr << "Usage: " << progname << std::endl;;
67  std::cerr << " Reads audio from stdin and return processed audio in stdout" << std::endl;
68  std::cerr << "Environment:"<< std::endl;;
69  std::cerr << " - S4STORE_PATH : path to store config [default: ./state]"<< std::endl;;
70  std::cerr << " - S4LOGINKEY : SOUND4 Login key (or S4USERID)"<< std::endl;;
71  std::cerr << " - RADIO_NAME : SOUND4 Radio Name"<< std::endl;;
72  std::cerr << " - S4_AWS_ACCESS_KEY_ID : SOUND4 Access Key ID"<< std::endl;;
73  std::cerr << " - S4_AWS_SECRET_ACCESS_KEY: SOUND4 Access Key Secret"<< std::endl;;
74  std::cerr << std::endl;
75  std::cerr << "Example:"<< std::endl;
76  std::cerr << "ffmpeg -re -i file.wav -ar "<<SAMPLE_RATE<<" -ac "<<CHANNEL_COUNT<<" -f f32le - | "<<progname<<" | ffplay -f f32le -ar "<<SAMPLE_RATE<<" -ac "<<CHANNEL_COUNT<<" -i -" << std::endl;;
77  #if defined(_WIN32) || defined(WIN32)
78  std::cerr << "ffmpeg -f dshow -i audio=\"Microphone\" -ar "<<SAMPLE_RATE<<" -ac "<<CHANNEL_COUNT<<" -f f32le - | "<<progname<<" | ffplay -f f32le -ar "<<SAMPLE_RATE<<" -ac "<<CHANNEL_COUNT<<" -i -" << std::endl;;
79  #else
80  std::cerr << "ffmpeg -f pulse -i default -ar "<<SAMPLE_RATE<<" -ac "<<CHANNEL_COUNT<<" -f f32le - | "<<progname<<" | ffplay -f f32le -ar "<<SAMPLE_RATE<<" -ac "<<CHANNEL_COUNT<<" -i -" << std::endl;;
81  #endif
82 }
83 
84 void MyLogger(impact_LogSeverity severe [[maybe_unused]], const char *str)
85 {
86  std::cerr << str << std::endl;
87 }
88 
89 
90 int main(int argc, char *const argv[])
91 {
92  struct impact_CInstance *pInstance = NULL;
93  const char *store_path_str=getenv("S4STORE_PATH");
94  // Get credentials from environment
95  char *pLoginKey=getenv("S4LOGINKEY");
96  if (!pLoginKey) {
97  pLoginKey=getenv("S4USERID");
98  }
99  char *pRadioName=getenv("RADIO_NAME");
100  char *pKeyId=getenv("S4_AWS_ACCESS_KEY_ID");
101  char *pAccessKey=getenv("S4_AWS_SECRET_ACCESS_KEY");
102 
103  const char *progname=argv[0];
104 
105  if (argc>1) {
106  if ((strcasecmp(argv[1],"-h")==0) || (strcasecmp(argv[1],"--help")==0)) {
107  Usage(progname);
108  return 0;
109  }
110  }
111  if (!pLoginKey || !pRadioName || !pKeyId || !pAccessKey) {
112  std::cerr << "Missing credentials in environment" << std::endl;
113  Usage(progname);
114  return 1;
115  }
116  std::filesystem::path fs_store_path;
117  if (!store_path_str || store_path_str[0]==0) {
118  fs_store_path=std::filesystem::current_path() / "state";
119  } else {
120  fs_store_path = std::filesystem::path(store_path_str);
121  }
122  if (!std::filesystem::exists(fs_store_path) || !std::filesystem::is_directory(fs_store_path)) {
123  std::cerr << "Storage folder '"<<fs_store_path.c_str()<<"' is not a valid directory" << std::endl;
124  Usage(progname);
125  return 1;
126  }
127  std::string store_path;
128  #ifdef _WIN32
129  {
130  // Convert path to UTF-8
131  std::wstring wstr(fs_store_path.wstring());
132  if (wstr.empty()) return {};
133  int size = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
134  std::string str(size, 0);
135  WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &str[0], size, NULL, NULL);
136  store_path=str;
137  }
138  #else // !_WIN32
139  store_path=fs_store_path;
140  #endif // !_WIN32
141 
143  // Optional: Use our own logging, instead of default color output to stderr.
144  //impact_SetLoggerCallback(&MyLogger);
145 
146  //impact_SetLogSeverity(impact_LogSeverity::info);
148 
149  std::cerr << "Process version: " << impact_GetVersion() << std::endl;
150  // Do some sanity checks
152  std::cerr << "Bad library sampling rate, expecting '"<<SAMPLE_RATE<<"' but has "<< impact_GetSampleRate() << std::endl;
153  return 1;
154  }
156  std::cerr << "Bad library frame size, expecting '"<<CHUNK_SIZE<<"' but has "<< impact_GetChunkSizeInFrames() << std::endl;
157  return 1;
158  }
160  std::cerr << "Bad library channel count, expecting '"<<CHANNEL_COUNT<<"' but has "<< impact_GetChannelCount() << std::endl;
161  return 1;
162  }
164 
166  // Setup parameters
167  struct impact_CParameters *params = impact_NewParameters();
168  // Optional: activate web authentication, set admin password
169  // This creates the authentication only for a new storage path
170  impact_SetParameter(params,"ADMIN_USER", "admin");
171  impact_SetParameter(params,"ADMIN_SECRET", "admin");
172 
173  // Create the processing instance
174  pInstance=impact_InitProcess2(pLoginKey,pRadioName,pKeyId,pAccessKey,store_path.c_str(), params);
175  impact_FreeParameters(params);
176  if (!pInstance) {
177  std::cerr << "Failed to create the process instance"<< std::endl;
178  return 1;
179  }
180 
181  // Start the update thread, without JSON server (low priority)
182  std::thread updater([pInstance](){
183  // will returned when stopped or destroyed
184  std::cerr << "impact_StartUpdateThread (no json port)" << std::endl;
185  impact_StartUpdateThread(pInstance, 0);
186  std::cerr << "impact_StartUpdateThread END" << std::endl;
187  });
188 
189  // This is not mandatory, but better be sure we are ready right now
190  std::cerr << "Waiting update thread" << std::endl;
191  if (impact_WaitUpdateThreadReady(pInstance, 5000)<0) {
192  std::cerr << "Timeout to create the process instance"<< std::endl;
193  }
194  std::cerr << "Waiting update thread DONE" << std::endl;
195 
196 #if (IMPACT_HAS_WEBSERVER)
198  // Starts a web server, listening on port http://localhost:8080, https://localhost:8443
199  std::cerr << "Starting web server" << std::endl;
200  int listenport= 8080;
201  int listenport_secure= 8443;
202  uint64_t webserver= SOUND4_INVALID_WEBSERVER_ID;
203  if (listenport || listenport_secure) {
204  webserver = impact_Webserver(listenport,listenport_secure,pInstance);
205  if (webserver==SOUND4_INVALID_WEBSERVER_ID) {
206  std::cerr << "Starting web server FAILED" << std::endl;
207  } else {
208  int web_status=impact_Webserver_Status(webserver);
209  if (listenport) {
210  if (web_status & SOUND4_WEBSERVER_HTTP_OK) {
211  std::cerr << "Web server listening on port " << listenport;
212  } else {
213  std::cerr << "Web server failed to listen on port " << listenport;
214  }
215  }
216  if (listenport_secure) {
217  if (web_status & SOUND4_WEBSERVER_HTTPS_OK) {
218  std::cerr << "Web server listening on secure port " << listenport_secure;
219  } else {
220  std::cerr << "Web server failed to listen on secure port " << listenport_secure;
221  }
222  }
223  std::cerr << "Starting web server DONE" << std::endl;
224  }
225  }
227 #endif // (IMPACT_HAS_WEBSERVER)
228 
229  // Force load a factory preset now
230  // Could be used instead of setting a store_path
231  /*{
232  impact_CClientInstance *pClient=impact_NewClient(pInstance);
233  const char *answer=impact_ProcessJson(pClient, "{ \"factoryload\": \"All Programs\" }", NULL);
234  std::cerr << "Loaded factory preset answer: " << answer<< std::endl;
235  impact_FreeJsonAnswer(answer);
236  impact_DeleteClient(pClient);
237  }*/
239 
240  std::cerr << "Estimated delay " << ( impact_GetEstimatedDelay(pInstance)*1000.0/SAMPLE_RATE ) << " ms" << std::endl;
241 
242  ProcessLoop(pInstance);
243 
245 #if (IMPACT_HAS_WEBSERVER)
246  // Stopping the webserver
247  std::cerr << "Stopping web server" << std::endl;
248  impact_Webserver_Stop(webserver, 1000);
249  std::cerr << "Stopping web server DONE" << std::endl;
250 #endif // (IMPACT_HAS_WEBSERVER)
251 
252  // Stopping the processing instance
253  std::cerr << "Stopping Update thread" << std::endl;
254  impact_StopUpdateThread(pInstance); // this will exit the update thread
255  std::cerr << "Stopping Update thread DONE" << std::endl;
256 
257  updater.join();
258 
259  impact_ExitProcess(pInstance);
261 
262  return 0;
263 }
void impact_ProcessAudio(struct impact_CInstance *instance, const float *input, float *output)
unsigned int impact_GetEstimatedDelay(struct impact_CInstance *instance)
const char * impact_GetVersion()
unsigned int impact_GetChunkSizeInFrames()
impact_LogSeverity
unsigned int impact_GetChannelCount()
void impact_SetLogSeverity(enum impact_LogSeverity severity)
unsigned int impact_GetSampleRate()
@ verbose5
void impact_SetParameter(struct impact_CParameters *params, const char *name, const char *value)
struct impact_CParameters * impact_NewParameters()
void impact_FreeParameters(struct impact_CParameters *params)
void impact_ExitProcess(struct impact_CInstance *instance)
struct impact_CInstance * impact_InitProcess2(const char *LoginKey, const char *RadioName, const char *AWS_Access_ID, const char *AWS_Access_Secret, const char *save_path, const struct impact_CParameters *parameters)
void impact_StopUpdateThread(struct impact_CInstance *instance)
void impact_StartUpdateThread(struct impact_CInstance *instance, unsigned int port)
int impact_WaitUpdateThreadReady(struct impact_CInstance *instance, int milliseconds)
#define SOUND4_WEBSERVER_HTTPS_OK
int impact_Webserver_Stop(uint64_t id, int timeout_ms)
uint64_t impact_Webserver(unsigned int listenport, unsigned int listenport_secure, struct impact_CInstance *instance)
int impact_Webserver_Status(uint64_t id)
#define SOUND4_WEBSERVER_HTTP_OK
#define SOUND4_INVALID_WEBSERVER_ID
#define CHANNEL_COUNT
void ProcessLoop(struct impact_CInstance *pInstance)
#define SAMPLE_RATE
void MyLogger(impact_LogSeverity severe[[maybe_unused]], const char *str)
void Usage(const char *progname)
#define CHUNK_SIZE
int main(int argc, char *const argv[])
C interface for library.