Warning: chmod(): Operation not permitted in /var/www/hopeinstoughton_www/wp-content/plugins/simple-universal-google-analytics/main.php on line 8 [0] in function chmod in /var/www/hopeinstoughton_www/wp-content/plugins/simple-universal-google-analytics/main.php on line 8 [1] in function include_once in /var/www/hopeinstoughton_www/wp-settings.php on line 560 [2] in function require_once in /var/www/hopeinstoughton_www/wp-config.php on line 85 [3] in function require_once in /var/www/hopeinstoughton_www/wp-load.php on line 50 [4] in function require_once in /var/www/hopeinstoughton_www/wp-blog-header.php on line 13 [5] in function require in /var/www/hopeinstoughton_www/index.php on line 17
| Server IP : 94.177.8.99 / Your IP : 216.73.217.165 Web Server : Apache/2.4.58 (Ubuntu) System : Linux aries 6.8.0-134-generic #134-Ubuntu SMP PREEMPT_DYNAMIC Fri Jun 26 18:43:11 UTC 2026 x86_64 User : www-data ( 33) PHP Version : 8.1.34 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/patientapps_www/modules/patientapps/model/ |
Upload File : |
<?php
// process an audio file
class pai_audio extends Model {
private $source;
private $targetfolder;
private $target;
private $mainfile;
const FFMPEG = '/usr/bin/ffmpeg';
public function __construct() {
parent::__construct();
$this->targetfolder = HOME . '/storage/patientapps/audio';
@mkdir($this->targetfolder, 0777, true);
}
// source filename
public function Source() {
if (func_num_args()) {
$this->source = func_get_arg(0);
return $this;
}
return $this->source;
}
// Target Procedure Abbreviation
public function Target() {
if (func_num_args()) {
$this->target = func_get_arg(0);
return $this;
}
return $this->target;
}
public function Process() {
$elements = pathinfo($this->source);
$ext = $elements['extension'];
$base = $elements['dirname'];
$this->mainfile = $this->target . '.' . $ext;
if (file_exists($base . '/final.flac')) {
$basename = $elements['basename'];
$dtarget = escapeshellarg($base . '/' . $basename);
$dsource = escapeshellarg($base . '/final.flac');
$command = self::FFMPEG . " -i {$dsource} -ab 192k -ar 22050 {$dtarget}";
`$command`;
$this->source = $base . '/final.flac';
$this->prepareMP3(); // mp3. tag it?
$target = $this->targetfolder . '/' . $this->target . '.cc';
if (!file_exists($target)) {
$content = file_get_contents($base . '/final.cc');
$content = $this->_trimContent($content); // remove trailing spaces
file_put_contents($target, $content);
}
} elseif (file_exists($this->source)) {
$this->prepareMainFile(); // copy wav to storage
$this->prepareMP3(); // mp3. tag it?
$this->prepareFlac(); // flac
$this->Transcribe(); // create CC file, if missing.
$this->Timings(); // edit the generated file manually, and make a copy of it
// to do: use ffmpeg to get duration?
}
return $this;
}
private function _trimContent($content) {
$output = '';
foreach(explode("\n", $content) as $line) {
$line = rtrim($line);
$output .= "$line\n";
}
return $output;
}
public function prepareMP3() {
$target = $this->targetfolder . '/' . $this->target . '.mp3';
$dsource = escapeshellarg($this->source);
$dtarget = escapeshellarg($target);
if (!file_exists($target)) {
$command = self::FFMPEG . " -i {$dsource} -ab 192k -ar 22050 {$dtarget}";
`$command`;
}
}
public function prepareFlac() { // used by speech to text
$target = $this->targetfolder . '/' . $this->target . '.flac';
$dsource = escapeshellarg($this->source);
$dtarget = escapeshellarg($target);
if (!file_exists($target)) {
$command = self::FFMPEG . " -i {$dsource} -acodec flac -b:a 32k -ac 1 -ar 16000 -compression_level 10 {$dtarget}";
`$command`;
$target = $this->targetfolder . '/' . $this->target . '.serial';
@unlink($target);
}
}
public function Transcribe() {
$target = $this->targetfolder . '/' . $this->target . '.serial';
if (!file_exists($target)) {
$creds = file_get_contents(__DIR__ . '/credentials.serial');
$creds = unserialize($creds);
$source = $this->targetfolder . '/' . $this->target . '.flac';
$output = $this->Watson_ProcessAudio($creds, $source);
file_put_contents($target, serialize($output));
}
}
public function Timings() {
$target = $this->targetfolder . '/' . $this->target . '.cc';
if (!file_exists($target)) {
$source = $this->targetfolder . '/' . $this->target . '.serial';
$result = $this->_transcribe_simplify($source);
file_put_contents($target, $result);
}
}
private function prepareMainFile() {
$dest = $this->targetfolder . '/' . $this->mainfile;
if (!file_exists($dest)) {
copy($this->source, $dest);
}
}
private function Watson_ProcessAudio($info, $flac_file) {
global $outfile;
$dir = __DIR__;
set_time_limit(600);
$url = 'https://stream.watsonplatform.net/speech-to-text/api/v1/recognize';
$url .= '?model=en-US_NarrowbandModel';
$url .= '×tamps=true';
$ch = curl_init($url);
$headers = array();
$headers[] = 'Content-Type: audio/flac';
$headers[] = "Transfer-Encoding: chunked";
$headers[] = 'X-Watson-Learning-Opt-Out: true';
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_BINARYTRANSFER, TRUE);
// send a file
curl_setopt($ch, CURLOPT_POST, true);
$data = file_get_contents($flac_file);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data );
curl_setopt($ch, CURLOPT_INFILESIZE, strlen($data));
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
curl_setopt($ch, CURLOPT_USERPWD, "apikey:{$info['apikey']}");
// curl_setopt($ch, CURLOPT_HEADER, 1);
// output the response
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$info = curl_exec($ch);
// var_dump($info);
// exit;
$info = json_decode($info, TRUE);
// close the session
curl_close($ch);
return $info;
}
/*
HTTP/1.1 100 Continue
X-EdgeConnect-MidMile-RTT: 1
X-EdgeConnect-Origin-MEX-Latency: 45
HTTP/1.1 500 Internal Server Error
Server: AkamaiGHost
Mime-Version: 1.0
Content-Type: text/html
Content-Length: 177
Expires: Fri, 12 Mar 2021 19:43:03 GMT
X-Reference-Error: 179.573a2f17.1615578183.5bdd5467
Date: Fri, 12 Mar 2021 19:43:03 GMT
Connection: close
<HTML><HEAD><TITLE>Error</TITLE></HEAD><BODY>
An error occurred while processing your request.<p>
Reference #179.573a2f17.1615578183.5bdd5467
</BODY></HTML>
*/
public function AsArray() {
$target = $this->targetfolder . '/' . $this->target . '.cc';
$cc = file_get_contents($target);
return [
'filename' => [
$this->mainfile,
$this->target . '.mp3',
$this->target . '.flac'
],
'cc' => $cc,
];
}
// word, from/to timeperiod (in ms)
// also total length (in ms)
private function _transcribe_simplify($target) {
$data = unserialize(file_get_contents($target));
$output = [];
if (isset($data['results'])) {
foreach($data['results'] as $segment) {
$this->_processAlternatives($output, $segment['alternatives'][0]['timestamps']);
}
// look for breaks
}
$result = $this->_breakintoPhrases($output);
$output = $this->_asText($result);
return $output;
}
private function _breakintoPhrases($words) {
$phrases = [];
$previous = 0;
$idx = 0;
foreach($words as $word) {
$gap = $word['start'] - $previous;
if ($gap >= 300) {
$idx++;
}
$phrases[$idx][] = $word;
$previous = $word['end'];
}
// join words in phrases together
$output = [];
foreach($phrases as $phrase) {
$c = count($phrase);
$current = $phrase[0];
for($i = 1; $i < $c; $i++) {
if (strlen($current['phrase'] . ' ' . $phrase[$i]['phrase']) > 30) {
$output[] = $current;
$current = $phrase[$i];
} else {
$current['phrase'] .= ' ' . $phrase[$i]['phrase'];
$current['end'] = $phrase[$i]['end'];
}
}
$output[] = $current;
}
return $output;
}
private function _asText($words) {
$output = '';
$previous = 0;
foreach($words as $word) {
$output .= "{$word['start']}-{$word['end']}={$word['phrase']}\n";
}
return $output;
}
private function _processAlternatives(&$output, $words) {
// $result = '';
foreach($words as $word) {
$start = intval($word[1] * 1000);
$end = intval($word[2] * 1000);
$output[] = [ 'start' => $start, 'end' => $end, 'phrase' => $word[0] ];
// $result .= "{$start}-{$end}={$word[0]}\n";
}
// return $result;
}
function filesize_formatted($size)
{
$units = array( 'B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');
$power = $size > 0 ? floor(log($size, 1024)) : 0;
return number_format($size / pow(1024, $power), 2, '.', ',') . ' ' . $units[$power];
}
private function _getJsonItemSize($filename, $entry) {
$content = json_decode(file_get_contents($filename), TRUE);
$content = $content[$entry] ?? '';
return strlen($content);
}
public function GetFilelist($subdomain, $fullhash) {
$result = [];
$files = glob($this->targetfolder . '/' . $subdomain . '.*');
foreach($files as $filename) {
$ext = pathinfo($filename, PATHINFO_EXTENSION);
$name = basename($filename);
if ($ext == 'serial') continue; // internal use only - placement of individual words in the audio file
if ($ext == 'flac') continue; // internal use only - used for speech to text
$stat = stat($filename);
$date = $stat['mtime'];
$size = $stat['size'];
$node = [
'name' => $name,
'date' => date('Y-m-d H:i', $date),
'fs' => $size,
'size' => $this->filesize_formatted($size),
];
switch($ext) {
case 'cc':
$node['action'] = 'edit captions';
break;
case 'sql3':
$node['action'] = 'sqlite3 database';
break;
case 'json';
// product json is the file containing page layouts for the app.
// the large json file created here contains this product json, and all the images.
$itemsize = $this->_getJsonItemSize($filename, 'data.json');
$node['action'] = '<a href="/patientapps/product-json/' . $fullhash . '" target="_blank" title="the product json is a subset of this json file - it does not contain the images">view product json</a>'.
' [<span title="This has a limit of 5MB">' . $this->filesize_formatted($itemsize) . '</span>]' ;
// $node['action'] = '<a href="/patientapps/product-toc/' . rawurlencode($name) . '" target="_blank">view json tree</a>';
$x = rawurlencode(str_replace('.json', '', $name));
$node['link'] = 'https://beta.patientapps.net/api/content/' . $x;
break;
}
$result[] = $node;
}
return !count($result) ? FALSE : $result;
}
}