How to set-up Mumble on your Synology server

Mumble is an free opensource solution for low latency voice communication, that is available for Synology users. There is also TeamSpeak and Ventrilo software, but there are no Synology packages available. So you would have to host yourself or rent the service.

But you probably thinking why can’t you just use Skype, Hangout or any other voice communication?

They are optimised in casual chatting than oppose to realtime communication, has higher latency, uses more bandwidth, and system resources. This would affect your communication in online play, where delays would hinder any team work communication.

Before we start make sure you have SynoCommunity added to your package sources, check out the SynoCommunity FAQ for an more in-depth tutorial:

SynoCommunity Package Sources

You should be able to see all SynoCommunity packages that are available, just install uMurmur

Package Center Community Tab

You now have Mumble on your Synology, open Mumble default port: 64738

Download Mumble:

Install as client, and run through the audio wizard to set-up your system.

You would notice that uMurmur on default has no password, and uses an default SSL certificate. You may wish to modify the configuration file of uMurmur to your specification. There is an easy way of doing so without accessing Telnet, but you would have to install an non-official package.

Download Mertymade Config File Editor from:
Mertymade Config File Editor

Do an manual installation
Package Center Manual Install

Package Center Upload Package

Ensure that you login as admin, just like you would in the terminal.
Config File Editor Admin User

Once you successfully logged in as admin, you should config the Config File Editor package use the dropdown menu:
Config File Editor

# Configfile for 'Config File Editor'
# format:
# <full path to file>,<name to display>

/usr/syno/etc/smb.conf,samba Daemon
/usr/syno/etc.defaults/smb.conf,samba Daemon-defaults
/etc/inetd.conf, inetd.conf
/etc.defaults/inetd.conf, inetd.conf-defaults
/usr/local/umurmur/var/umurmur.conf, Umurmur

Add line 30 (Depending on your DSM version), this will locate the configuration file for Umurmur. Save and reload the app to see it in the dropdown menu, you should have something like this:

max_bandwidth = 48000;
welcometext = "Welcome to uMurmur!";
certificate = "/usr/local/umurmur/var/umurmur.crt";
private_key = "/usr/local/umurmur/var/umurmur.key";
password = "";
# admin_password = "test";   # Set to enable admin functionality.
# ban_length = 0;            # Length in seconds for a ban. Default is 0. 0 = forever.
# enable_ban = false;        # Default is false
# banfile = "/usr/local/umurmur/var/banfile.txt";   # File to save bans to. Default is to not save bans to file.
# sync_banfile = false;      # Keep banfile synced. Default is false, which means it is saved to at shutdown only.
# allow_textmessage = true;  # Default is true
# opus_threshold = 100;      # Percentage of users supporting Opus codec for it to be chosen. Default is 100.
max_users = 10;

# bindport = 64738;
# bindaddr = "";

# username and groupname for privilege dropping.
# Will attempt to switch user if set.
username = "nobody";
# If groupname not set the user's default login group will be used
groupname = "nobody";

# Log to file option. Default is logging to syslog.
# umurmurd will close and reopen the logfile if SIGHUP is received.
logfile = "/usr/local/umurmur/var/umurmurd.log";

# Channel tree definition:
# Root channel must always be defined first.
# If a channel has a parent, the parent must be defined before the child channel(s).
channels = ( {
	 name = "Root";
	 parent = "";
	 description = "Root channel. No entry.";
	 noenter = true;
	 name = "Lobby";
	 parent = "Root";
	 description = "Lobby channel";
	 name = "Red team";
	 parent = "Lobby";
	 description = "The Red team channel";
	 # password = "redpw";
	 name = "Blue team";
	 parent = "Lobby";
	 description = "The Blue team channel";
	 # password = "bluepw";
# Channel links configuration.
channel_links = ( {
	 source = "Lobby";
	 destination = "Red team";
	 source = "Lobby";
	 destination = "Blue team";

# The channel in which users will appear in when connecting.
# Note that default channel can't have 'noenter = true' or password set
default_channel = "Lobby";

Default password:
Edit line 5

password = "example";

Enable Admin user:
Edit line 6, remove comment syntax.

admin_password = "test";   # Set to enable admin functionality.

Edit default SSL certificate and private key:
In the Config File Editor, add line

/usr/local/umurmur/var/umurmur.crt, Umurmur-Certificate
/usr/local/umurmur/var/umurmur.key, Umurmur-Private-Key

Use the dropdown menu and select Umurmur-Certificate, Umurmur-Private-Key. Copy the default file for safe keeping, before replacing it with your own. You could change the file location within Umurmur configuration file within /volume1/ on your Synology.

Currently I have yet to install the intermediate certificate, editing sslCA key with file location doesn’t work. Chaining certificate within the umurmur.crt does not work, I would assume this be a bug.

Simple UIPageViewController

To use UIPageViewController effectively you would need an UIPageViewControllerDataSource, this will help to display the before and after UIViewController. Here is an simple implementation to get you started:

#import <UIKit/UIKit.h>

@interface PYPageViewController : UIPageViewController
@property NSMutableArray *pages;
#import "PYPageViewController.h"

@interface PYPageViewController()

@implementation PYPageViewController
@synthesize pages;

- (void)viewWillAppear:(BOOL)animated {
    [self setDataSource:(id)self]; //Use this class as the data source
    self.pages  = [[NSMutableArray alloc]initWithCapacity:3];
    [self.pages addObject:[self.storyboard instantiateViewControllerWithIdentifier:@"PYRootViewController"]];
    [self.pages addObject:[self.storyboard instantiateViewControllerWithIdentifier:@"PYNextViewController"]];
    [self.pages addObject:[self.storyboard instantiateViewControllerWithIdentifier:@"PYEndViewController"]];
    [self setViewControllers:[NSArray arrayWithObject:pages[0]] direction:UIPageViewControllerNavigationDirectionForward animated:true completion:nil];
    [super viewWillAppear:animated];

/* UIPageViewControllerDataSource */
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {
    UIViewController *view = nil;

    if ([self.pages objectAtIndex:0] != viewController){
        for (NSUInteger i = [self.pages count]-1;i > 0 ; i--) {
            if ([self.pages objectAtIndex:i] == viewController){
                view = [self.pages objectAtIndex:i-1];
    return view;

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {
    UIViewController * view = nil;
    if ([self.pages objectAtIndex:[self.pages count]-1] != viewController){
        for (NSUInteger i = 0;i < [self.pages count]-1; i++) {
            if ([self.pages objectAtIndex:i] == viewController){
                view = [self.pages objectAtIndex:i+1];
    return view;


Extracting Data from Google Drive Spreadsheet into different formats

To simplify collaborators working with iOS development, I’ve decided that Google Spreadsheet is a good way of handling simple data collection. Which would then be exported to XML format for download, useful for updating applications on the fly.

Advantages for collaborators:

  • Spreadsheet operate within browser
  • Edit spreadsheet in real time, no modification conflict
  • Spreadsheet upload new revision (Publishes every 5 minute)
  • No learning of XML or PLIST
  • Set validation for each cell, restrict user error
  • User interface
  • Require PHP server
  • Ensure your PHP filesystem configuration of allow_url_fopen = “1”, to accept external links (Google domain). If not you can download and upload the CVS on your domain

Google has removed support for outputting RSS which was useful in creating XML. But there is an tutorial in exporting using CSV:

Rob has provided an brilliant example converting CSV into array, which can be used to convert it into JSON, XML, PLIST format

Would this be useful for your project?, can just check out this online example.


Use to retrieve the Google CSV file, separating each cell into arrays.

class CSV {
		// Function to convert CSV into associative array
		public function getArray($file, $delimiter = ",") {
			if (($handle = fopen($file, 'r')) !== FALSE) {
				$i = 0;
				while (($lineArray = fgetcsv($handle, 4000, $delimiter, '"')) !== FALSE) {
					for ($j = 0; $j < count($lineArray); $j++) {
						$arr[$i][$j] = $lineArray[$j];

				return $this->setKeys($arr);

		private function setKeys($data){
			$newArray = array();
			//Use first row for key value and remove from array
			$keys = array_shift($data);
			for ($j = 0; $j < count($data); $j++) {
				$tempArray = array_combine($keys, $data[$j]);
				// Remove values with no key, useful for commenting cells
				unset( $tempArray[null ] );

				$newArray[$j] = $tempArray;
			return $newArray;

Similar to XML but uses predefined variables in the XML tag, commonly used for Mac and iOS.

class PLIST {
        public function encodeObj($obj, $isDict = false) {
			if (is_bool($isDict) == false) {
				$isDict = false;
			$xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
			$xml .= "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"\">\n";
			$xml .= "<plist version=\"1.0\">\n";
            $xml .= self::encode($obj, "array", $isDict);
			$xml .= "</plist>";
            return $xml;

        private function encode($data, $node, $isDict, $depth = 0) {
            $xml .= str_repeat("\t", $depth);
            $xml .= "<$node>\n";
            foreach($data as $key => $val) {
                if(is_array($val) || is_object($val)) {
						$tempNode = "dict";
					} else {
						$tempNode = "array";
					$xml .= self::encode($val, $tempNode, $isDict, ($depth + 1));
                } else {
						$xml .= str_repeat("\t", ($depth + 1));
						$xml .= "<key>" . htmlspecialchars($key) . "</key>\n";
					$xml .= str_repeat("\t", ($depth + 1));
					if(strtolower($val) == "yes" || strtolower($val) == "true") {
						$xml .= "<true/>\n";
					} else if(strtolower($val) == "no" || strtolower($val) == "false") {
						$xml .= "<false/>\n";
					} else if($val == ""){
						$xml .= "<string/>\n";
					} else {
							if(strpos($val, ".") !== false){
								$variable = "real";
							} else {
								$variable = "integer";
						} else {
							$variable = "string";
						$xml .= "<$variable>" . htmlspecialchars($val) . "</$variable>\n";
            $xml .= str_repeat("\t", $depth);
            $xml .= "</$node>\n";
            return $xml;

Useful in testing different type of format, depending on the format chosen.


$format = ""; // hardcoding the type of format "json" "xml" "plist" "plist_dict"
$feed = ""; // Google CSV weblink

$obj = new csv();
$array = $obj->getArray($feed);

if (!$array){
	echo "\n############# CSV File is missing ##############\n";
} else if ($format == "json"){
	header("Content-type: text/json");
	echo json_encode($array),"\n";
} else if ($format == "xml"){
	header("Content-type: text/xml");
	$obj = new xml();
	echo $obj->encodeObj($array);
} else if ($format == "plist"){
	header("Content-type: text/xml");
	$obj = new plist();
	echo $obj->encodeObj($array);
} else if ($format == "plist_dict"){
	header("Content-type: text/xml");
	$obj = new plist();
	echo $obj->encodeObj($array, true);
} else {
	echo "\n############# Incorrect format ##############\n";

To change the format dynamically, you can use the address bar to pass the format value.
Modify the encode.php line:7

$format = $_GET["format"];


I have currently over 1000 entries within the Google Spreadsheet for a iPhone application project, takes about 0-2 seconds to download and replace the existing file. You would obviously have to re-organise this one level array into a more structured array for faster operations (Such as finding, removing, editing, moving …)