SOUND4 IMPACT.CL Library [1.0.5]
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 #endif
23 #include <sound4.impact.cl.h>
24 
25 #define SAMPLE_RATE 48000
26 #define CHUNK_SIZE 64
27 #define CHANNEL_COUNT 2
28 
29 // Reads from stdin, write to stdout
30 void ProcessLoop(struct impact_CInstance *pInstance)
31 {
33 bool stopthread=false;
34 alignas(64) float audio_in[CHUNK_SIZE*CHANNEL_COUNT];
35 alignas(64) float audio_out[CHUNK_SIZE*CHANNEL_COUNT];
36 
37  #if defined(_WIN32) || defined(WIN32)
38  // Windows: The stdin, stdout, and stderr streams always open in text mode by default
39  _setmode(_fileno(stdin), _O_BINARY);
40  _setmode(_fileno(stdout), _O_BINARY);
41  #endif
42 
43  while (!stopthread)
44  {
45  // Read a chunk from stdin
46  if (std::fread(&audio_in[0], sizeof(float), CHUNK_SIZE*CHANNEL_COUNT, stdin) != CHUNK_SIZE*CHANNEL_COUNT) {
47  std::cerr << "ProcessLoop: input stopped"<< std::endl;
48  stopthread=true;
49  break;
50  }
51  if (stopthread) break;
52  // Process it
53  impact_ProcessAudio(pInstance, audio_in, audio_out);
54  // Write it to stdout
55  if (fwrite(&audio_out[0], sizeof(float), CHUNK_SIZE*CHANNEL_COUNT, stdout) != CHUNK_SIZE*CHANNEL_COUNT) {
56  std::cerr << "Failed to write audio output" << std::endl;
57  stopthread=true;
58  }
59  }
61 }
62 
63 void Usage(const char *progname)
64 {
65  std::cerr << "Usage: " << progname << std::endl;;
66  std::cerr << " Reads audio from stdin and return processed audio in stdout" << std::endl;
67  std::cerr << "Environment:"<< std::endl;;
68  std::cerr << " - S4STORE_PATH : path to store config [default: ./state]"<< std::endl;;
69  std::cerr << " - S4LOGINKEY : SOUND4 Login key (or S4USERID)"<< std::endl;;
70  std::cerr << " - RADIO_NAME : SOUND4 Radio Name"<< std::endl;;
71  std::cerr << " - S4_AWS_ACCESS_KEY_ID : SOUND4 Access Key ID"<< std::endl;;
72  std::cerr << " - S4_AWS_SECRET_ACCESS_KEY: SOUND4 Access Key Secret"<< std::endl;;
73  std::cerr << std::endl;
74  std::cerr << "Example:"<< std::endl;
75  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;;
76  #if defined(_WIN32) || defined(WIN32)
77  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;;
78  #else
79  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;;
80  #endif
81 }
82 
83 void MyLogger(impact_LogSeverity severe [[maybe_unused]], const char *str)
84 {
85  std::cerr << str << std::endl;
86 }
87 
88 
89 int main(int argc, char *const argv[])
90 {
91  struct impact_CInstance *pInstance = NULL;
92  const char *store_path=getenv("S4STORE_PATH");
93  // Get credentials from environment
94  char *pLoginKey=getenv("S4LOGINKEY");
95  if (!pLoginKey) {
96  pLoginKey=getenv("S4USERID");
97  }
98  char *pRadioName=getenv("RADIO_NAME");
99  char *pKeyId=getenv("S4_AWS_ACCESS_KEY_ID");
100  char *pAccessKey=getenv("S4_AWS_SECRET_ACCESS_KEY");
101 
102  const char *progname=argv[0];
103 
104  if (argc>1) {
105  if ((strcasecmp(argv[1],"-h")==0) || (strcasecmp(argv[1],"--help")==0)) {
106  Usage(progname);
107  return 0;
108  }
109  }
110  if (!pLoginKey || !pRadioName || !pKeyId || !pAccessKey) {
111  std::cerr << "Missing credentials in environment" << std::endl;
112  Usage(progname);
113  return 1;
114  }
115  if (!store_path ) {
116  store_path="./state";
117  }
118  if (!std::filesystem::exists(store_path) || !std::filesystem::is_directory(store_path)) {
119  std::cerr << "Storage folder '"<<store_path<<"' is not a valid directory" << std::endl;
120  Usage(progname);
121  return 1;
122  }
123 
125  // Optional: Use our own logging, instead of default color output to stderr.
126  //impact_SetLoggerCallback(&MyLogger);
127 
128  //impact_SetLogSeverity(impact_LogSeverity::info);
130 
131  std::cerr << "Process version: " << impact_GetVersion() << std::endl;
132  // Do some sanity checks
134  std::cerr << "Bad library sampling rate, expecting '"<<SAMPLE_RATE<<"' but has "<< impact_GetSampleRate() << std::endl;
135  return 1;
136  }
138  std::cerr << "Bad library frame size, expecting '"<<CHUNK_SIZE<<"' but has "<< impact_GetChunkSizeInFrames() << std::endl;
139  return 1;
140  }
142  std::cerr << "Bad library channel count, expecting '"<<CHANNEL_COUNT<<"' but has "<< impact_GetChannelCount() << std::endl;
143  return 1;
144  }
146 
148  // Setup parameters
149  struct impact_CParameters *params = impact_NewParameters();
150  // Optional: activate web authentication, set admin password
151  // This creates the authentication only for a new storage path
152  impact_SetParameter(params,"ADMIN_USER", "admin");
153  impact_SetParameter(params,"ADMIN_SECRET", "admin");
154 
155  // Create the processing instance
156  pInstance=impact_InitProcess2(pLoginKey,pRadioName,pKeyId,pAccessKey,store_path, params);
157  impact_FreeParameters(params);
158  if (!pInstance) {
159  std::cerr << "Failed to create the process instance"<< std::endl;
160  return 1;
161  }
162 
163  // Start the update thread, without JSON server (low priority)
164  std::thread updater([pInstance](){
165  // will returned when stopped or destroyed
166  std::cerr << "impact_StartUpdateThread (no json port)" << std::endl;
167  impact_StartUpdateThread(pInstance, 0);
168  std::cerr << "impact_StartUpdateThread END" << std::endl;
169  });
170 
171  // This is not mandatory, but better be sure we are ready right now
172  std::cerr << "Waiting update thread" << std::endl;
173  if (impact_WaitUpdateThreadReady(pInstance, 5000)<0) {
174  std::cerr << "Timeout to create the process instance"<< std::endl;
175  }
176  std::cerr << "Waiting update thread DONE" << std::endl;
177 
178 #if (USE_WEBSERVER)
180  // Starts a web server, listening on port http://localhost:8080, https://localhost:8443
181  std::cerr << "Starting web server" << std::endl;
182  int listenport= 8080;
183  int listenport_secure= 8443;
184  uint64_t webserver= SOUND4_INVALID_WEBSERVER_ID;
185  if (listenport || listenport_secure) {
186  webserver = impact_Webserver(listenport,listenport_secure,pInstance);
187  if (webserver==SOUND4_INVALID_WEBSERVER_ID) {
188  std::cerr << "Starting web server FAILED" << std::endl;
189  } else {
190  int web_status=impact_Webserver_Status(webserver);
191  if (listenport) {
192  if (web_status & SOUND4_WEBSERVER_HTTP_OK) {
193  std::cerr << "Web server listening on port " << listenport;
194  } else {
195  std::cerr << "Web server failed to listen on port " << listenport;
196  }
197  }
198  if (listenport_secure) {
199  if (web_status & SOUND4_WEBSERVER_HTTPS_OK) {
200  std::cerr << "Web server listening on secure port " << listenport_secure;
201  } else {
202  std::cerr << "Web server failed to listen on secure port " << listenport_secure;
203  }
204  }
205  std::cerr << "Starting web server DONE" << std::endl;
206  }
207  }
209 #endif // (USE_WEBSERVER)
210 
211  // Force load a factory preset now
212  // Could be used instead of setting a store_path
213  /*{
214  impact_CClientInstance *pClient=impact_NewClient(pInstance);
215  const char *answer=impact_ProcessJson(pClient, "{ \"factoryload\": \"All Programs\" }", NULL);
216  std::cerr << "Loaded factory preset answer: " << answer<< std::endl;
217  impact_FreeJsonAnswer(answer);
218  impact_DeleteClient(pClient);
219  }*/
221 
222  std::cerr << "Estimated delay " << ( impact_GetEstimatedDelay(pInstance)*1000.0/SAMPLE_RATE ) << " ms" << std::endl;
223 
224  ProcessLoop(pInstance);
225 
227 #if (USE_WEBSERVER)
228  // Stopping the webserver
229  std::cerr << "Stopping web server" << std::endl;
230  impact_Webserver_Stop(webserver, 1000);
231  std::cerr << "Stopping web server DONE" << std::endl;
232 #endif // (USE_WEBSERVER)
233 
234  // Stopping the processing instance
235  std::cerr << "Stopping Update thread" << std::endl;
236  impact_StopUpdateThread(pInstance); // this will exit the update thread
237  std::cerr << "Stopping Update thread DONE" << std::endl;
238 
239  updater.join();
240 
241  impact_ExitProcess(pInstance);
243 
244  return 0;
245 }
void impact_ProcessAudio(struct impact_CInstance *instance, const float *input, float *output)
uint impact_GetEstimatedDelay(struct impact_CInstance *instance)
const char * impact_GetVersion()
uint impact_GetChunkSizeInFrames()
uint impact_GetChannelCount()
impact_LogSeverity
void impact_SetLogSeverity(enum impact_LogSeverity severity)
uint 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, uint 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)
int impact_Webserver_Status(uint64_t id)
#define SOUND4_WEBSERVER_HTTP_OK
#define SOUND4_INVALID_WEBSERVER_ID
uint64_t impact_Webserver(uint listenport, uint listenport_secure, struct impact_CInstance *instance)
#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.