SOUND4 x1.CLOUD Library [1.2.6]
sound4.x1.cloud-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.x1.cloud.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 cloudx1_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  cloudx1_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(cloudx1_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 cloudx1_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  //cloudx1_SetLoggerCallback(&MyLogger);
127 
128  //cloudx1_SetLogSeverity(cloudx1_LogSeverity::info);
130 
131  std::cerr << "Process version: " << cloudx1_GetVersion() << std::endl;
132  // Do some sanity checks
134  std::cerr << "Bad library sampling rate, expecting '"<<SAMPLE_RATE<<"' but has "<< cloudx1_GetSampleRate() << std::endl;
135  return 1;
136  }
138  std::cerr << "Bad library frame size, expecting '"<<CHUNK_SIZE<<"' but has "<< cloudx1_GetChunkSizeInFrames() << std::endl;
139  return 1;
140  }
142  std::cerr << "Bad library channel count, expecting '"<<CHANNEL_COUNT<<"' but has "<< cloudx1_GetChannelCount() << std::endl;
143  return 1;
144  }
146 
148  // Setup parameters
149  struct cloudx1_CParameters *params = cloudx1_NewParameters();
150  // Optional: activate web authentication, set admin password
151  // This creates the authentication only for a new storage path
152  cloudx1_SetParameter(params,"ADMIN_USER", "admin");
153  cloudx1_SetParameter(params,"ADMIN_SECRET", "admin");
154 
155  // Create the processing instance
156  pInstance=cloudx1_InitProcess2(pLoginKey,pRadioName,pKeyId,pAccessKey,store_path, params);
157  cloudx1_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 << "cloudx1_StartUpdateThread (no json port)" << std::endl;
167  cloudx1_StartUpdateThread(pInstance, 0);
168  std::cerr << "cloudx1_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 (cloudx1_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 = cloudx1_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=cloudx1_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  cloudx1_CClientInstance *pClient=cloudx1_NewClient(pInstance);
215  const char *answer=cloudx1_ProcessJson(pClient, "{ \"factoryload\": \"All Programs\" }", NULL);
216  std::cerr << "Loaded factory preset answer: " << answer<< std::endl;
217  cloudx1_FreeJsonAnswer(answer);
218  cloudx1_DeleteClient(pClient);
219  }*/
221 
222  std::cerr << "Estimated delay " << ( cloudx1_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  cloudx1_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  cloudx1_StopUpdateThread(pInstance); // this will exit the update thread
237  std::cerr << "Stopping Update thread DONE" << std::endl;
238 
239  updater.join();
240 
241  cloudx1_ExitProcess(pInstance);
243 
244  return 0;
245 }
void cloudx1_ProcessAudio(struct cloudx1_CInstance *instance, const float *input, float *output)
uint cloudx1_GetEstimatedDelay(struct cloudx1_CInstance *instance)
uint cloudx1_GetSampleRate()
uint cloudx1_GetChunkSizeInFrames()
const char * cloudx1_GetVersion()
cloudx1_LogSeverity
uint cloudx1_GetChannelCount()
void cloudx1_SetLogSeverity(enum cloudx1_LogSeverity severity)
@ verbose5
void cloudx1_ExitProcess(struct cloudx1_CInstance *instance)
void cloudx1_FreeParameters(struct cloudx1_CParameters *params)
struct cloudx1_CParameters * cloudx1_NewParameters()
void cloudx1_SetParameter(struct cloudx1_CParameters *params, const char *name, const char *value)
struct cloudx1_CInstance * cloudx1_InitProcess2(const char *LoginKey, const char *RadioName, const char *AWS_Access_ID, const char *AWS_Access_Secret, const char *save_path, const struct cloudx1_CParameters *parameters)
void cloudx1_StartUpdateThread(struct cloudx1_CInstance *instance, uint port)
int cloudx1_WaitUpdateThreadReady(struct cloudx1_CInstance *instance, int milliseconds)
void cloudx1_StopUpdateThread(struct cloudx1_CInstance *instance)
#define SOUND4_WEBSERVER_HTTPS_OK
int cloudx1_Webserver_Status(uint64_t id)
uint64_t cloudx1_Webserver(uint listenport, uint listenport_secure, struct cloudx1_CInstance *instance)
#define SOUND4_WEBSERVER_HTTP_OK
#define SOUND4_INVALID_WEBSERVER_ID
int cloudx1_Webserver_Stop(uint64_t id, int timeout_ms)
#define CHANNEL_COUNT
#define SAMPLE_RATE
void ProcessLoop(struct cloudx1_CInstance *pInstance)
void Usage(const char *progname)
void MyLogger(cloudx1_LogSeverity severe[[maybe_unused]], const char *str)
#define CHUNK_SIZE
int main(int argc, char *const argv[])
C interface for library.