protected function wavToMp3($data)
    {
        if (!file_exists(self::$lame_binary_path) || !is_executable(self::$lame_binary_path)) {
            throw new Exception('Lame binary "' . $this->lame_binary_path . '" does not exist or is not executable');
        }

        // size of wav data input
        $size = strlen($data);

        // file descriptors for reading and writing to the Lame process
        $descriptors = array(
                0 => array('pipe', 'r'), // stdin
                1 => array('pipe', 'w'), // stdout
                2 => array('pipe', 'a'), // stderr
        );

        if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
            // workaround for Windows conversion
            // writing to STDIN seems to hang indefinitely after writing approximately 0xC400 bytes
            $wavinput = tempnam(sys_get_temp_dir(), 'wav');
            if (!$wavinput) {
                throw new Exception('Failed to create temporary file for WAV to MP3 conversion');
            }
            file_put_contents($wavinput, $data);
            $size = 0;
        } else {
            $wavinput = '-'; // stdin
        }

        // Mono, variable bit rate, 32 kHz sampling rate, read WAV from stdin, write MP3 to stdout
        $cmd  = sprintf("%s -m m -v -b 32 %s -", self::$lame_binary_path, $wavinput);
        $proc = proc_open($cmd, $descriptors, $pipes);

        if (!is_resource($proc)) {
            throw new Exception('Failed to open process for MP3 encoding');
        }

        stream_set_blocking($pipes[0], 0); // set stdin to be non-blocking

        for ($written = 0; $written < $size; $written += $len) {
            // write to stdin until all WAV data is written
            $len = fwrite($pipes[0], substr($data, $written, 0x20000));

            if ($len === 0) {
                // fwrite wrote no data, make sure process is still alive, otherwise wait for it to process
                $status = proc_get_status($proc);
                if ($status['running'] === false) break;
                usleep(25000);
            } else if ($written < $size) {
                // couldn't write all data, small pause and try again
                usleep(10000);
            } else if ($len === false) {
                // fwrite failed, should not happen
                break;
            }
        }

        fclose($pipes[0]);

        $data = stream_get_contents($pipes[1]);
        $err  = trim(stream_get_contents($pipes[2]));

        fclose($pipes[1]);
        fclose($pipes[2]);

        $return = proc_close($proc);

        if ($wavinput != '-') unlink($wavinput); // delete temp file on Windows

        if ($return !== 0) {
            throw new Exception("Failed to convert WAV to MP3.  Shell returned ({$return}): {$err}");
        } else if ($written < $size) {
            throw new Exception('Failed to convert WAV to MP3.  Failed to write all data to encoder');
        }

        return $data;
    }