11 |
12 |
--------------------------------------------------------------------------------
/android/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/android/Snapcast/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/android/Snapcast/Ressources/Mute_Icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/android/Snapcast/Ressources/Speaker_Icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/android/Snapcast/Ressources/ic_launcher.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/Ressources/ic_launcher.zip
--------------------------------------------------------------------------------
/android/Snapcast/Ressources/ic_mute_icon.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/Ressources/ic_mute_icon.zip
--------------------------------------------------------------------------------
/android/Snapcast/Ressources/ic_speaker_icon.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/Ressources/ic_speaker_icon.zip
--------------------------------------------------------------------------------
/android/Snapcast/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /home/johannes/Develop/android-sdks/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/android/Snapcast/src/androidTest/java/de/badaix/snapcast/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package de.badaix.snapcast;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/android/Snapcast/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
15 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
43 |
46 |
47 |
51 |
54 |
55 |
59 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/android/Snapcast/src/main/assets/bin/arm64-v8a/librespot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/assets/bin/arm64-v8a/librespot
--------------------------------------------------------------------------------
/android/Snapcast/src/main/assets/bin/arm64-v8a/snapclient:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/assets/bin/arm64-v8a/snapclient
--------------------------------------------------------------------------------
/android/Snapcast/src/main/assets/bin/arm64-v8a/snapserver:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/assets/bin/arm64-v8a/snapserver
--------------------------------------------------------------------------------
/android/Snapcast/src/main/assets/bin/armeabi/librespot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/assets/bin/armeabi/librespot
--------------------------------------------------------------------------------
/android/Snapcast/src/main/assets/bin/armeabi/snapclient:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/assets/bin/armeabi/snapclient
--------------------------------------------------------------------------------
/android/Snapcast/src/main/assets/bin/armeabi/snapserver:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/assets/bin/armeabi/snapserver
--------------------------------------------------------------------------------
/android/Snapcast/src/main/assets/bin/mips/librespot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/assets/bin/mips/librespot
--------------------------------------------------------------------------------
/android/Snapcast/src/main/assets/bin/mips/snapclient:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/assets/bin/mips/snapclient
--------------------------------------------------------------------------------
/android/Snapcast/src/main/assets/bin/mips/snapserver:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/assets/bin/mips/snapserver
--------------------------------------------------------------------------------
/android/Snapcast/src/main/assets/bin/x86/librespot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/assets/bin/x86/librespot
--------------------------------------------------------------------------------
/android/Snapcast/src/main/assets/bin/x86/snapclient:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/assets/bin/x86/snapclient
--------------------------------------------------------------------------------
/android/Snapcast/src/main/assets/bin/x86/snapserver:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/assets/bin/x86/snapserver
--------------------------------------------------------------------------------
/android/Snapcast/src/main/assets/files/Snapcast_800.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/assets/files/Snapcast_800.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/java/de/badaix/snapcast/AboutActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of snapcast
3 | * Copyright (C) 2014-2017 Johannes Pohl
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | package de.badaix.snapcast;
20 |
21 | import android.content.pm.PackageInfo;
22 | import android.content.pm.PackageManager;
23 | import android.os.Bundle;
24 | import android.support.v7.app.AppCompatActivity;
25 | import android.util.Log;
26 | import android.webkit.WebView;
27 |
28 | public class AboutActivity extends AppCompatActivity {
29 | private static final String TAG = "About";
30 |
31 | @Override
32 | protected void onCreate(Bundle savedInstanceState) {
33 | super.onCreate(savedInstanceState);
34 | setContentView(R.layout.activity_about);
35 | try {
36 | getSupportActionBar().setTitle(getString(R.string.about) + " Snapcast");
37 | } catch (Exception e) {
38 | Log.wtf(TAG, "Setting title", e);
39 | }
40 | PackageInfo pInfo;
41 | try {
42 | pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
43 | getSupportActionBar().setSubtitle("v" + pInfo.versionName);
44 | } catch (PackageManager.NameNotFoundException e) {
45 | Log.wtf(TAG, "Setting subtitle", e);
46 | }
47 | WebView wv = (WebView) findViewById(R.id.webView);
48 | wv.loadUrl("file:///android_asset/" + this.getText(R.string.about_file));
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/android/Snapcast/src/main/java/de/badaix/snapcast/BootCompleteReceiver.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of snapcast
3 | * Copyright (C) 2014-2017 Johannes Pohl
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | package de.badaix.snapcast;
20 |
21 | import android.content.BroadcastReceiver;
22 | import android.content.Context;
23 | import android.content.Intent;
24 | import android.text.TextUtils;
25 |
26 | import de.badaix.snapcast.utils.Settings;
27 |
28 | /**
29 | * Created by johannes on 05.05.16.
30 | */
31 | public class BootCompleteReceiver extends BroadcastReceiver {
32 | @Override
33 | public void onReceive(Context context, Intent intent) {
34 | if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {
35 | Settings settings = new Settings(context);
36 | if (settings.isAutostart()) {
37 | String host = settings.getHost();
38 | int port = settings.getStreamPort();
39 | if (TextUtils.isEmpty(host))
40 | return;
41 |
42 | Intent i = new Intent(context, SnapClientService.class);
43 | i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
44 | i.putExtra(SnapClientService.EXTRA_HOST, host);
45 | i.putExtra(SnapClientService.EXTRA_PORT, port);
46 | i.setAction(SnapClientService.ACTION_START);
47 |
48 | context.startService(i);
49 | }
50 | }
51 | }
52 | }
53 |
54 |
55 |
--------------------------------------------------------------------------------
/android/Snapcast/src/main/java/de/badaix/snapcast/ClientSettingsActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of snapcast
3 | * Copyright (C) 2014-2017 Johannes Pohl
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | package de.badaix.snapcast;
20 |
21 | import android.app.Activity;
22 | import android.content.Intent;
23 | import android.os.Bundle;
24 | import android.support.v7.app.AppCompatActivity;
25 | import android.view.MenuItem;
26 |
27 | /**
28 | * Created by johannes on 11.01.16.
29 | */
30 | public class ClientSettingsActivity extends AppCompatActivity {
31 | private ClientSettingsFragment clientSettingsFragment = null;
32 |
33 | @Override
34 | protected void onCreate(Bundle savedInstanceState) {
35 | super.onCreate(savedInstanceState);
36 | clientSettingsFragment = new ClientSettingsFragment();
37 | clientSettingsFragment.setArguments(getIntent().getExtras());
38 |
39 | // Display the fragment as the main content.
40 | getFragmentManager().beginTransaction()
41 | .replace(android.R.id.content, clientSettingsFragment)
42 | .commit();
43 | }
44 |
45 | @Override
46 | public boolean onOptionsItemSelected(MenuItem item) {
47 | if (item.getItemId() == android.R.id.home) {
48 | onBackPressed();
49 | return true;
50 | }
51 | return false;
52 | }
53 |
54 | @Override
55 | public void onBackPressed() {
56 | Intent intent = new Intent();
57 | intent.putExtra("client", clientSettingsFragment.getClient().toJson().toString());
58 | intent.putExtra("clientOriginal", clientSettingsFragment.getOriginalClientInfo().toJson().toString());
59 | setResult(Activity.RESULT_OK, intent);
60 | finish();
61 | // super.onBackPressed();
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/android/Snapcast/src/main/java/de/badaix/snapcast/GroupSettingsActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of snapcast
3 | * Copyright (C) 2014-2017 Johannes Pohl
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | package de.badaix.snapcast;
20 |
21 | import android.app.Activity;
22 | import android.content.Intent;
23 | import android.os.Bundle;
24 | import android.support.v7.app.AppCompatActivity;
25 | import android.view.MenuItem;
26 |
27 | /**
28 | * Created by johannes on 06.12.16.
29 | */
30 |
31 | public class GroupSettingsActivity extends AppCompatActivity {
32 |
33 | private GroupSettingsFragment groupSettingsFragment;
34 |
35 | @Override
36 | protected void onCreate(Bundle savedInstanceState) {
37 | super.onCreate(savedInstanceState);
38 |
39 | groupSettingsFragment = new GroupSettingsFragment();
40 | groupSettingsFragment.setArguments(getIntent().getExtras());
41 | // Display the fragment as the main content.
42 | getFragmentManager().beginTransaction()
43 | .replace(android.R.id.content, groupSettingsFragment).commit();
44 | }
45 |
46 | @Override
47 | public boolean onOptionsItemSelected(MenuItem item) {
48 | if (item.getItemId() == android.R.id.home) {
49 | onBackPressed();
50 | return true;
51 | }
52 | return false;
53 | }
54 |
55 | @Override
56 | public void onBackPressed() {
57 | Intent intent = new Intent();
58 | intent.putExtra("group", groupSettingsFragment.getGroup().getId());
59 | if (groupSettingsFragment.didStreamChange())
60 | intent.putExtra("stream", groupSettingsFragment.getStream());
61 | if (groupSettingsFragment.didClientsChange())
62 | intent.putStringArrayListExtra("clients", groupSettingsFragment.getClients());
63 | setResult(Activity.RESULT_OK, intent);
64 | finish();
65 | }
66 | }
67 |
68 |
--------------------------------------------------------------------------------
/android/Snapcast/src/main/java/de/badaix/snapcast/control/json/JsonSerialisable.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of snapcast
3 | * Copyright (C) 2014-2017 Johannes Pohl
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | package de.badaix.snapcast.control.json;
20 |
21 | import org.json.JSONObject;
22 |
23 | /**
24 | * Created by johannes on 08.01.16.
25 | */
26 | public interface JsonSerialisable {
27 | public void fromJson(JSONObject json);
28 |
29 | public JSONObject toJson();
30 | }
31 |
--------------------------------------------------------------------------------
/android/Snapcast/src/main/java/de/badaix/snapcast/control/json/Snapclient.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of snapcast
3 | * Copyright (C) 2014-2017 Johannes Pohl
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | package de.badaix.snapcast.control.json;
20 |
21 | import org.json.JSONObject;
22 |
23 | /**
24 | * Created by johannes on 06.01.16.
25 | */
26 | public class Snapclient extends Snapcast {
27 |
28 | public Snapclient() {
29 | super();
30 | }
31 |
32 | public Snapclient(JSONObject json) {
33 | super(json);
34 | }
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/android/Snapcast/src/main/java/de/badaix/snapcast/control/json/Snapserver.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of snapcast
3 | * Copyright (C) 2014-2017 Johannes Pohl
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | package de.badaix.snapcast.control.json;
20 |
21 | import android.util.Log;
22 |
23 | import org.json.JSONException;
24 | import org.json.JSONObject;
25 |
26 | /**
27 | * Created by johannes on 06.01.16.
28 | */
29 | public class Snapserver extends Snapcast {
30 | private static final String TAG = "Snapserver";
31 |
32 | int controlProtocolVersion = 1;
33 |
34 | public Snapserver() {
35 | super();
36 | }
37 |
38 | public Snapserver(JSONObject json) {
39 | super(json);
40 | }
41 |
42 | @Override
43 | public void fromJson(JSONObject json) {
44 | try {
45 | super.fromJson(json);
46 | controlProtocolVersion = json.getInt("controlProtocolVersion");
47 | } catch (JSONException e) {
48 | Log.wtf(TAG, "Getting from JSON", e);
49 | }
50 | }
51 |
52 | @Override
53 | public JSONObject toJson() {
54 | JSONObject json = super.toJson();
55 | try {
56 | json.put("controlProtocolVersion", controlProtocolVersion);
57 | } catch (JSONException e) {
58 | Log.wtf(TAG, "Saving to JSON", e);
59 | }
60 | return json;
61 | }
62 |
63 | public int getControlProtocolVersion() {
64 | return controlProtocolVersion;
65 | }
66 |
67 | @Override
68 | public boolean equals(Object o) {
69 | if (this == o) return true;
70 | if (o == null || getClass() != o.getClass()) return false;
71 |
72 | Snapserver that = (Snapserver) o;
73 |
74 | if (controlProtocolVersion != that.controlProtocolVersion) return false;
75 | return super.equals(o);
76 | }
77 |
78 | @Override
79 | public int hashCode() {
80 | int result = super.hashCode();
81 | result = 31 * result + controlProtocolVersion;
82 | return result;
83 | }
84 | }
85 |
86 |
--------------------------------------------------------------------------------
/android/Snapcast/src/main/java/uk/org/jaggard/snapcast/.gitignore:
--------------------------------------------------------------------------------
1 | AdDetails.java
2 |
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-hdpi/ic_media_pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-hdpi/ic_media_pause.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-hdpi/ic_media_play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-hdpi/ic_media_play.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-hdpi/ic_media_stop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-hdpi/ic_media_stop.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-hdpi/ic_mute_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-hdpi/ic_mute_icon.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-hdpi/ic_settings_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-hdpi/ic_settings_black_24dp.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-hdpi/ic_settings_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-hdpi/ic_settings_white_24dp.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-hdpi/ic_speaker_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-hdpi/ic_speaker_icon.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-mdpi/ic_media_pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-mdpi/ic_media_pause.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-mdpi/ic_media_play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-mdpi/ic_media_play.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-mdpi/ic_media_stop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-mdpi/ic_media_stop.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-mdpi/ic_mute_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-mdpi/ic_mute_icon.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-mdpi/ic_settings_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-mdpi/ic_settings_black_24dp.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-mdpi/ic_settings_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-mdpi/ic_settings_white_24dp.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-mdpi/ic_speaker_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-mdpi/ic_speaker_icon.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-xhdpi/ic_media_pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-xhdpi/ic_media_pause.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-xhdpi/ic_media_play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-xhdpi/ic_media_play.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-xhdpi/ic_media_stop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-xhdpi/ic_media_stop.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-xhdpi/ic_mute_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-xhdpi/ic_mute_icon.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-xhdpi/ic_settings_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-xhdpi/ic_settings_black_24dp.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-xhdpi/ic_settings_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-xhdpi/ic_settings_white_24dp.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-xhdpi/ic_speaker_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-xhdpi/ic_speaker_icon.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-xxhdpi/ic_media_pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-xxhdpi/ic_media_pause.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-xxhdpi/ic_media_play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-xxhdpi/ic_media_play.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-xxhdpi/ic_media_stop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-xxhdpi/ic_media_stop.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-xxhdpi/ic_mute_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-xxhdpi/ic_mute_icon.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-xxhdpi/ic_settings_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-xxhdpi/ic_settings_black_24dp.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-xxhdpi/ic_settings_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-xxhdpi/ic_settings_white_24dp.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-xxhdpi/ic_speaker_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-xxhdpi/ic_speaker_icon.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-xxxhdpi/ic_mute_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-xxxhdpi/ic_mute_icon.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-xxxhdpi/ic_settings_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-xxxhdpi/ic_settings_black_24dp.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-xxxhdpi/ic_settings_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-xxxhdpi/ic_settings_white_24dp.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable-xxxhdpi/ic_speaker_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/drawable-xxxhdpi/ic_speaker_icon.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/drawable/ic_menu_overflow_material.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
25 |
28 |
29 |
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/layout/activity_about.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
25 |
26 |
30 |
31 |
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/layout/dialog_local_server_settings.xml:
--------------------------------------------------------------------------------
1 |
19 |
20 |
25 |
26 |
29 |
30 |
36 |
37 |
45 |
46 |
47 |
50 |
51 |
57 |
58 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/layout/fragment_group_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
25 |
26 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/menu/menu_snapcast.xml:
--------------------------------------------------------------------------------
1 |
41 |
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/menu/menu_snapcast_server.xml:
--------------------------------------------------------------------------------
1 |
18 |
19 |
40 |
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/Snapcast/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/values-de/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Snapcast
3 | Snapclient gestartet
4 | Snapclient
5 | Snapclient läuft…
6 | Info…
7 | Stop
8 | Details
9 | Löschen
10 | Gruppe
11 |
12 | Snapcast
13 |
14 | Über…
15 | Einstellungen…
16 | Suche nach Server
17 | Aktualisiere Client-Liste
18 | Verstecke offline Clients
19 |
20 | Client Einstellungen
21 | Name
22 | MAC
23 | ID
24 | IP
25 | Host
26 | Betriebssystem
27 | Version
28 | Zuletzt gesehen
29 | Latenz
30 | Online
31 |
32 | Gruppe
33 |
34 | Play/Stop
35 | Client %1$s gelöscht
36 | Stream ist nicht in nativer sample rate: %1$d\nNative sample rate: %2$d
37 | Streams in nicht-nativer sample rate sind evtl. nicht synchron
38 | Rückgängig
39 | Über
40 | files/about.html
41 | Über
42 |
43 | Stream
44 | Warnung
45 | Dies ist ein Test-Client.\nNicht für den Produktiv-Einsatz vorgesehen.
46 | Host
47 | Stream-Port
48 | Control-Port
49 | Snapserver host
50 | Auto-start Snapclient
51 | Verbinden
52 |
53 |
54 |
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/values-ja/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Snapcast
3 | Snapclient 開始しました
4 | Snapclient
5 | Snapclient は実行中です…
6 | 情報…
7 | 停止
8 | 詳細
9 | 削除
10 |
11 | Snapcast
12 |
13 | アプリについて…
14 | 設定…
15 | サーバーのスキャン
16 | クライアント一覧の更新
17 | オフラインのクライアントを非表示
18 |
19 | クライアント設定
20 | 名前
21 | MAC
22 | IP
23 | ホスト
24 | OS
25 | バージョン
26 | 最後の視聴
27 | レイテンシー
28 | オンライン
29 | 再生/停止
30 | クライアント %1$s を削除しました
31 | ストリームはネイティブのサンプリングレートではありません: %1$d\nネイティブのサンプリングレート: %2$d
32 | ネイティブのサンプリングレートではないストリームは同期して再生されないことがあります
33 | 元に戻す
34 | アプリについて
35 | files/about.html
36 | アプリについて
37 |
38 | ストリーム
39 | 警告
40 | これはテストクライアントです。\n正式利用用途ではありません。
41 | ホスト
42 | ストリーム ポート
43 | コントロール ポート
44 | Snapserver ホスト
45 | Snapclient を自動開始
46 |
47 |
48 |
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 | >
2 |
3 |
9 |
10 |
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #607D8B
4 | #455A64
5 | #FF4081
6 | #ff999999
7 | #ffffffff
8 | #8c666666
9 |
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 16dp
6 | 16dp
7 | 8dp
8 |
9 |
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1
4 | 2
5 | 3
6 |
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 | #EEEEEE
3 | #37474F
4 |
5 |
11 |
12 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/xml/client_preferences.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
12 |
17 |
22 |
27 |
32 |
37 |
42 |
47 |
48 |
--------------------------------------------------------------------------------
/android/Snapcast/src/main/res/xml/group_preferences.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/android/Snapcast/src/test/java/de/badaix/snapcast/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package de.badaix.snapcast;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.assertEquals;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/android/Snapcast/version.properties:
--------------------------------------------------------------------------------
1 | #Thu Feb 08 00:28:47 GMT 2018
2 | VERSION_NAME=0.14
3 | VERSION_BUILD=109
4 | VERSION_CODE=1408
5 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | google()
6 | jcenter()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.1.0'
10 |
11 | // NOTE: Do not place your application dependencies here; they belong
12 | // in the individual module build.gradle files
13 | }
14 | }
15 |
16 | allprojects {
17 | repositories {
18 | jcenter()
19 | maven {
20 | url "https://maven.google.com"
21 | }
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
28 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | org.gradle.parallel=true
19 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Mar 29 22:17:46 BST 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
7 |
--------------------------------------------------------------------------------
/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':Snapcast'
2 |
--------------------------------------------------------------------------------
/buildroot/Config.in:
--------------------------------------------------------------------------------
1 | source "$BR2_EXTERNAL_SNAPCAST_PATH/package/snapcast/Config.in"
2 |
--------------------------------------------------------------------------------
/buildroot/configs/snapcast_defconfig:
--------------------------------------------------------------------------------
1 | # Snapcast Packages
2 | BR2_GCC_VERSION_4_9_X=y
3 | BR2_GCC_VERSION="4.9.4"
4 | BR2_TOOLCHAIN_GCC_AT_LEAST="4.9"
5 | BR2_TOOLCHAIN_BUILDROOT_GLIBC=y
6 | BR2_PACKAGE_DBUS=y
7 | BR2_PACKAGE_EXPAT=y
8 | BR2_PACKAGE_LIBDAEMON=y
9 | BR2_PACKAGE_AVAHI=y
10 | BR2_PACKAGE_AVAHI_AUTOIPD=y
11 | BR2_PACKAGE_AVAHI_DAEMON=y
12 | BR2_PACKAGE_AVAHI_LIBDNSSD_COMPATIBILITY=y
13 | BR2_PACKAGE_ALSA_LIB=y
14 | BR2_PACKAGE_ALSA_LIB_DEVDIR="/dev/snd"
15 | BR2_PACKAGE_ALSA_LIB_PCM_PLUGINS="all"
16 | BR2_PACKAGE_ALSA_LIB_CTL_PLUGINS="all"
17 | BR2_PACKAGE_ALSA_LIB_ALOAD=y
18 | BR2_PACKAGE_ALSA_LIB_MIXER=y
19 | BR2_PACKAGE_ALSA_LIB_PCM=y
20 | BR2_PACKAGE_ALSA_LIB_RAWMIDI=y
21 | BR2_PACKAGE_ALSA_LIB_HWDEP=y
22 | BR2_PACKAGE_ALSA_LIB_SEQ=y
23 | BR2_PACKAGE_ALSA_LIB_ALISP=y
24 | BR2_PACKAGE_ALSA_LIB_OLD_SYMBOLS=y
25 | BR2_PACKAGE_ALSA_UTILS=y
26 | BR2_PACKAGE_ALSA_UTILS_ALSACONF=y
27 | BR2_PACKAGE_ALSA_UTILS_ALSACTL=y
28 | BR2_PACKAGE_ALSA_UTILS_AMIXER=y
29 | BR2_PACKAGE_ALSA_UTILS_APLAY=y
30 | BR2_PACKAGE_ALSA_UTILS_SPEAKER_TEST=y
31 | BR2_PACKAGE_LIBCURL=y
32 | BR2_PACKAGE_LIBAO=y
33 | BR2_PACKAGE_LIBVORBIS=y
34 | BR2_PACKAGE_VORBIS_TOOLS=y
35 | BR2_PACKAGE_FLAC=y
36 | BR2_PACKAGE_LIBOGG=y
37 | BR2_PACKAGE_TREMOR=y
38 | BR2_PACKAGE_SNAPCAST=y
39 |
40 | # Other Packages
41 |
--------------------------------------------------------------------------------
/buildroot/external.desc:
--------------------------------------------------------------------------------
1 | name: SNAPCAST
2 |
--------------------------------------------------------------------------------
/buildroot/external.mk:
--------------------------------------------------------------------------------
1 | include $(sort $(wildcard $(BR2_EXTERNAL_SNAPCAST_PATH)/package/*/*.mk))
2 |
--------------------------------------------------------------------------------
/buildroot/package/snapcast/Config.in:
--------------------------------------------------------------------------------
1 | config BR2_PACKAGE_SNAPCAST
2 | bool "snapcast"
3 | help
4 | Synchronous multi-room audio player
5 |
--------------------------------------------------------------------------------
/buildroot/package/snapcast/S99snapclient:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 |
3 | start() {
4 | echo -n "Starting snapclient: "
5 | start-stop-daemon -S -q -b -x snapclient
6 | [ $? = 0 ] && echo "OK" || echo "FAIL"
7 | }
8 |
9 | stop() {
10 | echo -n "Stopping snapclient: "
11 | start-stop-daemon -K -q -x snapclient
12 | [ $? = 0 ] && echo "OK" || echo "FAIL"
13 | }
14 |
15 | restart() {
16 | stop
17 | start
18 | }
19 |
20 | case "$1" in
21 | start)
22 | start
23 | ;;
24 | stop)
25 | stop
26 | ;;
27 | restart|reload)
28 | restart
29 | ;;
30 | *)
31 | echo "Usage: $0 {start|stop|restart}"
32 | exit 1
33 | esac
34 |
35 | exit $?
36 |
--------------------------------------------------------------------------------
/buildroot/package/snapcast/snapcast.mk:
--------------------------------------------------------------------------------
1 | ################################################################################
2 | #
3 | # snapcast
4 | #
5 | ################################################################################
6 |
7 | SNAPCAST_VERSION = master
8 | SNAPCAST_SITE = https://github.com/badaix/snapcast
9 | SNAPCAST_SITE_METHOD = git
10 | SNAPCAST_DEPENDENCIES = libogg alsa-lib # libstdcpp libavahi-client libatomic libflac libvorbisidec
11 | SNAPCAST_LICENSE = GPLv3
12 | SNAPCAST_LICENSE_FILES = COPYING
13 |
14 | # http://lists.busybox.net/pipermail/buildroot/2013-March/069811.html
15 | define SNAPCAST_EXTRACT_CMDS
16 | rm -rf $(@D)
17 | (git clone --depth 1 $(SNAPCAST_SITE) $(@D) && \
18 | cd $(@D)/externals && \
19 | git submodule update --init --recursive)
20 | touch $(@D)/.stamp_downloaded
21 | endef
22 |
23 | define SNAPCAST_BUILD_CMDS
24 | $(TARGET_CONFIGURE_OPTS) $(MAKE) -C $(@D)/client TARGET=BUILDROOT
25 | endef
26 |
27 | define SNAPCAST_INSTALL_TARGET_CMDS
28 | $(INSTALL) -m 0755 -D $(@D)/client/snapclient $(TARGET_DIR)/usr/bin/snapclient
29 | $(INSTALL) -m 0755 -D $(SNAPCAST_PKGDIR)/S99snapclient $(TARGET_DIR)/etc/init.d/S99snapclient
30 | endef
31 |
32 | $(eval $(generic-package))
33 |
--------------------------------------------------------------------------------
/client/browseZeroConf/browseAvahi.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 | #ifndef BROWSEAVAHI_H
19 | #define BROWSEAVAHI_H
20 |
21 | #include
22 | #include
23 |
24 | #include
25 | #include
26 | #include
27 |
28 | class BrowseAvahi;
29 |
30 | #include "browsemDNS.h"
31 |
32 | class BrowseAvahi : public BrowsemDNS
33 | {
34 | public:
35 | BrowseAvahi();
36 | ~BrowseAvahi();
37 | bool browse(const std::string& serviceName, mDNSResult& result, int timeout) override;
38 |
39 | private:
40 | void cleanUp();
41 | static void resolve_callback(AvahiServiceResolver *r, AVAHI_GCC_UNUSED AvahiIfIndex interface, AVAHI_GCC_UNUSED AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, AVAHI_GCC_UNUSED void* userdata);
42 | static void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, void* userdata);
43 | static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata);
44 | AvahiClient* client_;
45 | mDNSResult result_;
46 | AvahiServiceBrowser* sb_;
47 | };
48 |
49 | #endif
50 |
--------------------------------------------------------------------------------
/client/browseZeroConf/browseBonjour.h:
--------------------------------------------------------------------------------
1 | #ifndef BROWSEBONJOUR_H
2 | #define BROWSEBONJOUR_H
3 |
4 | #include
5 |
6 | class BrowseBonjour;
7 |
8 | #include "browsemDNS.h"
9 |
10 | class BrowseBonjour : public BrowsemDNS
11 | {
12 | public:
13 | bool browse(const std::string& serviceName, mDNSResult& result, int timeout) override;
14 | };
15 | #endif
16 |
--------------------------------------------------------------------------------
/client/browseZeroConf/browsemDNS.h:
--------------------------------------------------------------------------------
1 | #ifndef BROWSEMDNS_H
2 | #define BROWSEMDNS_H
3 |
4 | #include
5 |
6 | struct mDNSResult
7 | {
8 | int proto_;
9 | std::string ip_;
10 | std::string host_;
11 | uint16_t port_;
12 | bool valid_;
13 | };
14 |
15 | class BrowsemDNS
16 | {
17 | public:
18 | virtual bool browse(const std::string& serviceName, mDNSResult& result, int timeout) = 0;
19 | };
20 |
21 | #if defined(HAS_AVAHI)
22 | #include "browseAvahi.h"
23 | typedef BrowseAvahi BrowseZeroConf;
24 | #elif defined(HAS_BONJOUR)
25 | #include "browseBonjour.h"
26 | typedef BrowseBonjour BrowseZeroConf;
27 | #endif
28 |
29 | #endif
30 |
--------------------------------------------------------------------------------
/client/build_android.sh:
--------------------------------------------------------------------------------
1 | #/bin/sh
2 |
3 | if [ -z "$NDK_DIR_ARM" ] && [ -z "$NDK_DIR_ARM64" ] && [ -z "$NDK_DIR_MIPS" ] && [ -z "$NDK_DIR_X86" ]; then
4 | echo "Specify at least one NDK_DIR_[ARM|ARM64|MIPS|X86]"
5 | exit
6 | fi
7 |
8 | if [ -n "$NDK_DIR_ARM" ]; then
9 | export NDK_DIR="$NDK_DIR_ARM"
10 | export ARCH=arm
11 | make clean; make TARGET=ANDROID -j 4; mv ./snapclient ../android/Snapcast/src/main/assets/bin/armeabi/
12 | fi
13 |
14 | if [ -n "$NDK_DIR_ARM64" ]; then
15 | export NDK_DIR="$NDK_DIR_ARM64"
16 | export ARCH=aarch64
17 | make clean; make TARGET=ANDROID -j 4; mv ./snapclient ../android/Snapcast/src/main/assets/bin/arm64-v8a/
18 | fi
19 |
20 | if [ -n "$NDK_DIR_MIPS" ]; then
21 | export NDK_DIR="$NDK_DIR_MIPS"
22 | export ARCH=mips
23 | make clean; make TARGET=ANDROID -j 4; mv ./snapclient ../android/Snapcast/src/main/assets/bin/mips/
24 | fi
25 |
26 | if [ -n "$NDK_DIR_X86" ]; then
27 | export NDK_DIR="$NDK_DIR_X86"
28 | export ARCH=x86
29 | make clean; make TARGET=ANDROID -j 4; mv ./snapclient ../android/Snapcast/src/main/assets/bin/x86/
30 | fi
31 |
--------------------------------------------------------------------------------
/client/build_android_all.sh:
--------------------------------------------------------------------------------
1 | #/bin/sh
2 |
3 | export NDK_DIR_ARM="$1-arm"
4 | export NDK_DIR_ARM64="$1-arm64"
5 | export NDK_DIR_MIPS="$1-mips"
6 | export NDK_DIR_X86="$1-x86"
7 |
8 | ./build_android.sh
9 |
--------------------------------------------------------------------------------
/client/controller.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef CONTROLLER_H
20 | #define CONTROLLER_H
21 |
22 | #include
23 | #include
24 | #include "decoder/decoder.h"
25 | #include "message/message.h"
26 | #include "message/serverSettings.h"
27 | #include "player/pcmDevice.h"
28 | #ifdef HAS_ALSA
29 | #include "player/alsaPlayer.h"
30 | #elif HAS_OPENSL
31 | #include "player/openslPlayer.h"
32 | #elif HAS_COREAUDIO
33 | #include "player/coreAudioPlayer.h"
34 | #endif
35 | #include "clientConnection.h"
36 | #include "stream.h"
37 |
38 |
39 | /// Forwards PCM data to the audio player
40 | /**
41 | * Sets up a connection to the server (using ClientConnection)
42 | * Sets up the audio decoder and player.
43 | * Decodes audio (message_type::kWireChunk) and feeds PCM to the audio stream buffer
44 | * Does timesync with the server
45 | */
46 | class Controller : public MessageReceiver
47 | {
48 | public:
49 | Controller(const std::string& clientId, size_t instance);
50 | void start(const PcmDevice& pcmDevice, const std::string& host, size_t port, int latency);
51 | void stop();
52 |
53 | /// Implementation of MessageReceiver.
54 | /// ClientConnection passes messages from the server through these callbacks
55 | virtual void onMessageReceived(ClientConnection* connection, const msg::BaseMessage& baseMessage, char* buffer);
56 |
57 | /// Implementation of MessageReceiver.
58 | /// Used for async exception reporting
59 | virtual void onException(ClientConnection* connection, shared_exception_ptr exception);
60 |
61 | private:
62 | void worker();
63 | bool sendTimeSyncMessage(long after = 1000);
64 | std::string hostId_;
65 | size_t instance_;
66 | std::atomic active_;
67 | std::thread controllerThread_;
68 | SampleFormat sampleFormat_;
69 | PcmDevice pcmDevice_;
70 | int latency_;
71 | std::unique_ptr clientConnection_;
72 | std::shared_ptr stream_;
73 | std::unique_ptr decoder_;
74 | std::unique_ptr player_;
75 | std::shared_ptr serverSettings_;
76 | std::shared_ptr headerChunk_;
77 | std::mutex receiveMutex_;
78 |
79 | shared_exception_ptr async_exception_;
80 | };
81 |
82 |
83 | #endif
84 |
85 |
--------------------------------------------------------------------------------
/client/debian/compat:
--------------------------------------------------------------------------------
1 | 9
2 |
--------------------------------------------------------------------------------
/client/debian/control:
--------------------------------------------------------------------------------
1 | Source: snapclient
2 | Section: utils
3 | Priority: extra
4 | Maintainer: Johannes Pohl
5 | Build-Depends: debhelper (>= 9.0.0), libc6-dev, dh-systemd, libavahi-client3 (>= 0.6.16), libflac8 (>= 1.3.0), libogg0 (>= 1.0rc3), libvorbis0a (>= 1.1.2)
6 | Standards-Version: 3.8.4
7 | Homepage: https://github.com/badaix/snapcast
8 |
9 | Package: snapclient
10 | Architecture: any
11 | Depends: adduser, ${shlibs:Depends}, ${misc:Depends}
12 | Description: Snapcast client
13 | Snapcast is a multi-room client-server audio player, where all clients are
14 | time synchronized with the server to play perfectly synced audio. It's not a
15 | standalone player, but an extension that turns your existing audio player into
16 | a Sonos-like multi-room solution.
17 | .
18 | The server's audio input is a named pipe `/tmp/snapfifo`. All data that is fed
19 | into this file will be send to the connected clients. One of the most generic
20 | ways to use Snapcast is in conjunction with the music player daemon or Mopidy,
21 | which can be configured to use a named pipe as audio output.
22 |
--------------------------------------------------------------------------------
/client/debian/copyright:
--------------------------------------------------------------------------------
1 | This work was written and packaged by Johannes Pohl
2 |
3 | It was downloaded from:
4 |
5 | Upstream Author: Johannes Pohl
6 |
7 | Copyright:
8 |
9 |
10 |
11 | License:
12 |
13 | This program is free software: you can redistribute it and/or modify
14 | it under the terms of the GNU General Public License as published by
15 | the Free Software Foundation, either version 3 of the License, or
16 | (at your option) any later version.
17 |
18 | This package is distributed in the hope that it will be useful,
19 | but WITHOUT ANY WARRANTY; without even the implied warranty of
20 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 | GNU General Public License for more details.
22 |
23 | You should have received a copy of the GNU General Public License
24 | along with this program. If not, see .
25 |
26 | On Debian systems, the complete text of the GNU General
27 | Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
28 |
29 | The Debian packaging is:
30 |
31 | Copyright (C) 2014-2017 Johannes Pohl
32 |
33 | and is licensed under the GPL version 3, see above.
34 |
35 |
--------------------------------------------------------------------------------
/client/debian/docs:
--------------------------------------------------------------------------------
1 | ../README.md
2 |
--------------------------------------------------------------------------------
/client/debian/rules:
--------------------------------------------------------------------------------
1 | #!/usr/bin/make -f
2 | # -*- makefile -*-
3 | # Sample debian/rules that uses debhelper.
4 | # This file was originally written by Joey Hess and Craig Small.
5 | # As a special exception, when this file is copied by dh-make into a
6 | # dh-make output file, you may use that output file without restriction.
7 | # This special exception was added by Craig Small in version 0.37 of dh-make.
8 |
9 | # Uncomment this to turn on verbose mode.
10 | # export DH_VERBOSE=1
11 |
12 | %:
13 | dh $@ --with=systemd
14 |
--------------------------------------------------------------------------------
/client/debian/snapclient.default:
--------------------------------------------------------------------------------
1 | # defaults file for snapclient
2 |
3 | # start snapclient automatically?
4 | START_SNAPCLIENT=true
5 |
6 | # Allowed options:
7 | # --help produce help message
8 | # -v, --version show version number
9 | # -h, --host arg server hostname or ip address
10 | # -p, --port arg (=1704) server port
11 | # -l, --list list pcm devices
12 | # -s, --soundcard arg (=default) index or name of the soundcard
13 | # -d, --daemon [=arg(=-3)] daemonize, optional process priority [-20..19]
14 | # --user arg the user[:group] to run snapclient as when daemonized
15 | # --latency arg (=0) latency of the soundcard
16 | # -i, --instance arg (=1) instance id
17 |
18 | USER_OPTS="--user snapclient:audio"
19 |
20 | SNAPCLIENT_OPTS=""
21 |
--------------------------------------------------------------------------------
/client/debian/snapclient.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Label
6 | de.badaix.snapcast.snapclient
7 | ProgramArguments
8 |
9 | /usr/local/bin/snapclient
10 |
11 |
12 | RunAtLoad
13 |
14 | KeepAlive
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/client/debian/snapclient.postinst:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | umask 0022
6 | ACTION="$1"
7 | VERSION="$2"
8 |
9 | if [ "$ACTION" = "configure" ]; then
10 | # add the snapclient user
11 | if ! getent passwd snapclient >/dev/null; then
12 | useradd --gid audio --system --home-dir /var/lib/snapclient snapclient
13 | fi
14 | fi
15 |
16 | #DEBHELPER#
17 |
--------------------------------------------------------------------------------
/client/debian/snapclient.postrm:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | if [ "$1" = "purge" ]; then
6 | # clean up all statoverrides that belonged to snapclient, so the
7 | # snapclient system user can be removed cleanly
8 | for i in $(dpkg-statoverride --list | awk '/^snapclient / {print $4}'); do
9 | if dpkg-statoverride --list --quiet "$i" >/dev/null; then
10 | dpkg-statoverride --force --quiet --remove "$i"
11 | fi
12 | done
13 | fi
14 |
15 | userdel --force snapclient > /dev/null || true
16 |
17 | #DEBHELPER#
18 |
--------------------------------------------------------------------------------
/client/debian/snapclient.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Snapcast client
3 | After=network-online.target sound.target
4 | Requires=network-online.target
5 |
6 | [Service]
7 | EnvironmentFile=-/etc/default/snapclient
8 | Type=forking
9 | ExecStart=/usr/bin/snapclient -d $USER_OPTS $SNAPCLIENT_OPTS
10 | PIDFile=/var/run/snapclient/pid
11 | Restart=always
12 |
13 | [Install]
14 | WantedBy=multi-user.target
15 |
--------------------------------------------------------------------------------
/client/decoder/decoder.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef DECODER_H
20 | #define DECODER_H
21 | #include
22 | #include "message/pcmChunk.h"
23 | #include "message/codecHeader.h"
24 | #include "common/sampleFormat.h"
25 |
26 |
27 | class Decoder
28 | {
29 | public:
30 | Decoder() {};
31 | virtual ~Decoder() {};
32 | virtual bool decode(msg::PcmChunk* chunk) = 0;
33 | virtual SampleFormat setHeader(msg::CodecHeader* chunk) = 0;
34 |
35 | protected:
36 | std::mutex mutex_;
37 | };
38 |
39 |
40 | #endif
41 |
42 |
43 |
--------------------------------------------------------------------------------
/client/decoder/flacDecoder.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef FLAC_DECODER_H
20 | #define FLAC_DECODER_H
21 |
22 | #include "decoder.h"
23 |
24 | #include
25 | #include
26 | #include
27 |
28 |
29 |
30 | struct CacheInfo
31 | {
32 | CacheInfo() : sampleRate_(0)
33 | {
34 | reset();
35 | }
36 |
37 | void reset()
38 | {
39 | isCachedChunk_ = true;
40 | cachedBlocks_ = 0;
41 | }
42 |
43 | bool isCachedChunk_;
44 | size_t cachedBlocks_;
45 | size_t sampleRate_;
46 | };
47 |
48 |
49 | class FlacDecoder : public Decoder
50 | {
51 | public:
52 | FlacDecoder();
53 | virtual ~FlacDecoder();
54 | virtual bool decode(msg::PcmChunk* chunk);
55 | virtual SampleFormat setHeader(msg::CodecHeader* chunk);
56 |
57 | CacheInfo cacheInfo_;
58 | std::unique_ptr lastError_;
59 | };
60 |
61 |
62 | #endif
63 |
64 |
65 |
--------------------------------------------------------------------------------
/client/decoder/oggDecoder.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef OGG_DECODER_H
20 | #define OGG_DECODER_H
21 | #include "decoder.h"
22 | #ifdef HAS_TREMOR
23 | #include
24 | #else
25 | #include
26 | #endif
27 |
28 |
29 | class OggDecoder : public Decoder
30 | {
31 | public:
32 | OggDecoder();
33 | virtual ~OggDecoder();
34 | virtual bool decode(msg::PcmChunk* chunk);
35 | virtual SampleFormat setHeader(msg::CodecHeader* chunk);
36 |
37 | private:
38 | bool decodePayload(msg::PcmChunk* chunk);
39 | template
40 | T clip(const T& value, const T& lower, const T& upper) const
41 | {
42 | if (value > upper) return upper;
43 | if (value < lower) return lower;
44 | return value;
45 | }
46 |
47 | ogg_sync_state oy; /// sync and verify incoming physical bitstream
48 | ogg_stream_state os; /// take physical pages, weld into a logical stream of packets
49 | ogg_page og; /// one Ogg bitstream page. Vorbis packets are inside
50 | ogg_packet op; /// one raw packet of data for decode
51 |
52 | vorbis_info vi; /// struct that stores all the static vorbis bitstream settings
53 | vorbis_comment vc; /// struct that stores all the bitstream user comments
54 | vorbis_dsp_state vd; /// central working state for the packet->PCM decoder
55 | vorbis_block vb; /// local working space for packet->PCM decode
56 |
57 | SampleFormat sampleFormat_;
58 | };
59 |
60 |
61 | #endif
62 |
63 |
64 |
--------------------------------------------------------------------------------
/client/decoder/pcmDecoder.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef PCM_DECODER_H
20 | #define PCM_DECODER_H
21 | #include "decoder.h"
22 |
23 |
24 | class PcmDecoder : public Decoder
25 | {
26 | public:
27 | PcmDecoder();
28 | virtual bool decode(msg::PcmChunk* chunk);
29 | virtual SampleFormat setHeader(msg::CodecHeader* chunk);
30 | };
31 |
32 |
33 | #endif
34 |
35 |
36 |
--------------------------------------------------------------------------------
/client/player/alsaPlayer.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef ALSA_PLAYER_H
20 | #define ALSA_PLAYER_H
21 |
22 | #include "player.h"
23 | #include
24 |
25 |
26 | /// Audio Player
27 | /**
28 | * Audio player implementation using Alsa
29 | */
30 | class AlsaPlayer : public Player
31 | {
32 | public:
33 | AlsaPlayer(const PcmDevice& pcmDevice, std::shared_ptr stream);
34 | virtual ~AlsaPlayer();
35 |
36 | /// Set audio volume in range [0..1]
37 | virtual void start();
38 | virtual void stop();
39 |
40 | /// List the system's audio output devices
41 | static std::vector pcm_list(void);
42 |
43 | protected:
44 | virtual void worker();
45 |
46 | private:
47 | void initAlsa();
48 | void uninitAlsa();
49 |
50 | snd_pcm_t* handle_;
51 | snd_pcm_uframes_t frames_;
52 | char *buff_;
53 | };
54 |
55 |
56 | #endif
57 |
58 |
--------------------------------------------------------------------------------
/client/player/coreAudioPlayer.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef CORE_AUDIO_PLAYER_H
20 | #define CORE_AUDIO_PLAYER_H
21 |
22 | #include
23 | #include
24 | #include
25 |
26 | #include "player.h"
27 |
28 | /// Audio Player
29 | /**
30 | * Audio player implementation using CoreAudio
31 | *
32 | * Warning:
33 | *
34 | * !! This player is experimental and might not be maintained !!
35 | *
36 | */
37 | class CoreAudioPlayer : public Player
38 | {
39 | public:
40 | CoreAudioPlayer(const PcmDevice& pcmDevice, std::shared_ptr stream);
41 | virtual ~CoreAudioPlayer();
42 |
43 | void playerCallback(AudioQueueRef queue, AudioQueueBufferRef bufferRef);
44 |
45 | protected:
46 | virtual void worker();
47 | void initAudioQueue();
48 | void uninitAudioQueue(AudioQueueRef queue);
49 |
50 | AudioQueueTimelineRef timeLine_;
51 | size_t ms_;
52 | size_t frames_;
53 | size_t buff_size_;
54 | std::shared_ptr pubStream_;
55 | long lastChunkTick;
56 | };
57 |
58 |
59 | #endif
60 |
61 |
--------------------------------------------------------------------------------
/client/player/openslPlayer.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef OPEN_SL_PLAYER_H
20 | #define OPEN_SL_PLAYER_H
21 |
22 | #include
23 | #include
24 | #include
25 |
26 | #include "player.h"
27 |
28 | typedef int (*AndroidAudioCallback)(short *buffer, int num_samples);
29 |
30 |
31 | /// OpenSL Audio Player
32 | /**
33 | * Player implementation for OpenSL (e.g. Android)
34 | */
35 | class OpenslPlayer : public Player
36 | {
37 | public:
38 | OpenslPlayer(const PcmDevice& pcmDevice, std::shared_ptr stream);
39 | virtual ~OpenslPlayer();
40 |
41 | virtual void start();
42 | virtual void stop();
43 |
44 | void playerCallback(SLAndroidSimpleBufferQueueItf bq);
45 |
46 | protected:
47 | void initOpensl();
48 | void uninitOpensl();
49 |
50 | virtual void worker();
51 | void throwUnsuccess(const std::string& what, SLresult result);
52 | std::string resultToString(SLresult result) const;
53 |
54 | // engine interfaces
55 | SLObjectItf engineObject;
56 | SLEngineItf engineEngine;
57 | SLObjectItf outputMixObject;
58 |
59 | // buffer queue player interfaces
60 | SLObjectItf bqPlayerObject;
61 | SLPlayItf bqPlayerPlay;
62 | SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
63 | SLVolumeItf bqPlayerVolume;
64 |
65 | // Double buffering.
66 | int curBuffer;
67 | char *buffer[2];
68 |
69 | size_t ms_;
70 | size_t frames_;
71 | size_t buff_size;
72 | std::shared_ptr pubStream_;
73 | };
74 |
75 |
76 | #endif
77 |
78 |
--------------------------------------------------------------------------------
/client/player/pcmDevice.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef PCM_DEVICE_H
20 | #define PCM_DEVICE_H
21 |
22 | #include
23 |
24 |
25 | struct PcmDevice
26 | {
27 | PcmDevice() : idx(-1){};
28 | int idx;
29 | std::string name;
30 | std::string description;
31 | };
32 |
33 |
34 | #endif
35 |
36 |
--------------------------------------------------------------------------------
/client/player/player.cpp:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #include
20 | #include
21 |
22 | #include "player.h"
23 | #include "aixlog.hpp"
24 |
25 |
26 | using namespace std;
27 |
28 |
29 | Player::Player(const PcmDevice& pcmDevice, std::shared_ptr stream) :
30 | active_(false),
31 | stream_(stream),
32 | pcmDevice_(pcmDevice),
33 | volume_(1.0),
34 | muted_(false),
35 | volCorrection_(1.0)
36 | {
37 | }
38 |
39 |
40 |
41 | void Player::start()
42 | {
43 | active_ = true;
44 | playerThread_ = thread(&Player::worker, this);
45 | }
46 |
47 |
48 | Player::~Player()
49 | {
50 | stop();
51 | }
52 |
53 |
54 |
55 | void Player::stop()
56 | {
57 | if (active_)
58 | {
59 | active_ = false;
60 | playerThread_.join();
61 | }
62 | }
63 |
64 |
65 | void Player::adjustVolume(char* buffer, size_t frames)
66 | {
67 | double volume = volume_;
68 | if (muted_)
69 | volume = 0.;
70 |
71 | const SampleFormat& sampleFormat = stream_->getFormat();
72 |
73 | if ((volume < 1.0) || (volCorrection_ != 1.))
74 | {
75 | volume *= volCorrection_;
76 | if (sampleFormat.sampleSize == 1)
77 | adjustVolume(buffer, frames*sampleFormat.channels, volume);
78 | else if (sampleFormat.sampleSize == 2)
79 | adjustVolume(buffer, frames*sampleFormat.channels, volume);
80 | else if (sampleFormat.sampleSize == 4)
81 | adjustVolume(buffer, frames*sampleFormat.channels, volume);
82 | }
83 | }
84 |
85 |
86 | //http://stackoverflow.com/questions/1165026/what-algorithms-could-i-use-for-audio-volume-level
87 | void Player::setVolume(double volume)
88 | {
89 | double base = M_E;
90 | // double base = 10.;
91 | volume_ = (pow(base, volume)-1) / (base-1);
92 | LOG(DEBUG) << "setVolume: " << volume << " => " << volume_ << "\n";
93 | }
94 |
95 |
96 |
97 | void Player::setMute(bool mute)
98 | {
99 | muted_ = mute;
100 | }
101 |
102 |
103 |
--------------------------------------------------------------------------------
/client/player/player.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef PLAYER_H
20 | #define PLAYER_H
21 |
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include "stream.h"
27 | #include "pcmDevice.h"
28 | #include "common/endian.h"
29 | #include "aixlog.hpp"
30 |
31 |
32 | /// Audio Player
33 | /**
34 | * Abstract audio player implementation
35 | */
36 | class Player
37 | {
38 | public:
39 | Player(const PcmDevice& pcmDevice, std::shared_ptr stream);
40 | virtual ~Player();
41 |
42 | /// Set audio volume in range [0..1]
43 | virtual void setVolume(double volume);
44 | virtual void setMute(bool mute);
45 | virtual void start();
46 | virtual void stop();
47 |
48 | protected:
49 | virtual void worker() = 0;
50 |
51 | template
52 | void adjustVolume(char *buffer, size_t count, double volume)
53 | {
54 | T* bufferT = (T*)buffer;
55 | for (size_t n=0; n(endian::swap(bufferT[n]) * volume);
57 | }
58 |
59 | void adjustVolume(char* buffer, size_t frames);
60 |
61 | std::atomic active_;
62 | std::shared_ptr stream_;
63 | std::thread playerThread_;
64 | PcmDevice pcmDevice_;
65 | double volume_;
66 | bool muted_;
67 | double volCorrection_;
68 | };
69 |
70 |
71 | #endif
72 |
73 |
--------------------------------------------------------------------------------
/client/snapclient.1:
--------------------------------------------------------------------------------
1 | .\"groff -Tascii -man snapclient.1
2 | .TH SNAPCLIENT 1 "December 2015"
3 | .SH NAME
4 | snapclient - Snapcast client
5 | .SH SYNOPSIS
6 | \fBsnapclient\fR [\fIOPTION\fR]...
7 | .SH DESCRIPTION
8 | Snapcast is a multi-room client-server audio player, where all clients are
9 | time synchronized with the server to play perfectly synced audio. It's not a
10 | standalone player, but an extension that turns your existing audio player into
11 | a Sonos-like multi-room solution.
12 | .br
13 | The server's audio input is a named pipe `/tmp/snapfifo`. All data that is fed
14 | into this file will be send to the connected clients. One of the most generic
15 | ways to use Snapcast is in conjunction with the music player daemon or Mopidy,
16 | which can be configured to use a named pipe as audio output.
17 | .SS Options
18 | .TP
19 | \fB--help\fR
20 | produce help message
21 | .TP
22 | \fB-v, --version\fR
23 | show version number
24 | .TP
25 | \fB-l, --list\fR
26 | list pcm devices
27 | .TP
28 | \fB-h, --host\fR
29 | server hostname or ip address
30 | .TP
31 | \fB-p, --port\fR
32 | server port
33 | .TP
34 | \fB-s, --soundcard\fR
35 | index or name of the soundcard
36 | .TP
37 | \fB-d, --daemon\fR
38 | daemonize, optional process priority [-20..19]
39 | .TP
40 | \fB--user\fR
41 | the user[:group] to run snapclient as when daemonized
42 | .TP
43 | \fB--latency\fR
44 | latency of the soundcard
45 | .SH FILES
46 | .TP
47 | \fI/etc/default/snapclient\fR
48 | the daemon default configuration file
49 | .SH "COPYRIGHT"
50 | Copyright (C) 2014-2017 Johannes Pohl (johannes.pohl@badaix.de).
51 | License GPLv3+: GNU GPL version 3 or later .
52 | This is free software: you are free to change and redistribute it.
53 | There is NO WARRANTY, to the extent permitted by law.
54 | .SH "SEE ALSO"
55 | snapserver(1)
56 |
--------------------------------------------------------------------------------
/client/timeProvider.cpp:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #include "timeProvider.h"
20 | #include "aixlog.hpp"
21 |
22 |
23 | TimeProvider::TimeProvider() : diffToServer_(0)
24 | {
25 | diffBuffer_.setSize(200);
26 | }
27 |
28 |
29 | void TimeProvider::setDiff(const tv& c2s, const tv& s2c)
30 | {
31 | tv latency = c2s - s2c;
32 | double diff = latency.sec * 1000. + latency.usec / 1000.;
33 | setDiffToServer(diff / 2.);
34 | }
35 |
36 |
37 | void TimeProvider::setDiffToServer(double ms)
38 | {
39 | static int32_t lastTimeSync = 0;
40 | timeval now;
41 | chronos::systemtimeofday(&now);
42 |
43 | /// clear diffBuffer if last update is older than a minute
44 | if (!diffBuffer_.empty() && (std::abs(now.tv_sec - lastTimeSync) > 60))
45 | {
46 | LOG(INFO) << "Last time sync older than a minute. Clearing time buffer\n";
47 | diffToServer_ = ms*1000;
48 | diffBuffer_.clear();
49 | }
50 | lastTimeSync = now.tv_sec;
51 |
52 | diffBuffer_.add(ms*1000);
53 | diffToServer_ = diffBuffer_.median(3);
54 | // LOG(INFO) << "setDiffToServer: " << ms << ", diff: " << diffToServer_ / 1000.f << "\n";
55 | }
56 |
57 | /*
58 | long TimeProvider::getPercentileDiffToServer(size_t percentile)
59 | {
60 | return diffBuffer.percentile(percentile);
61 | }
62 | */
63 |
64 |
--------------------------------------------------------------------------------
/client/timeProvider.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef TIME_PROVIDER_H
20 | #define TIME_PROVIDER_H
21 |
22 | #include
23 | #include
24 | #include "doubleBuffer.h"
25 | #include "message/message.h"
26 | #include "common/timeDefs.h"
27 |
28 |
29 | /// Provides local and server time
30 | /**
31 | * Stores time difference to the server
32 | * Returns server's local system time.
33 | * Clients are using the server time to play audio in sync, independent of the client's system time
34 | */
35 | class TimeProvider
36 | {
37 | public:
38 | static TimeProvider& getInstance()
39 | {
40 | static TimeProvider instance;
41 | return instance;
42 | }
43 |
44 | void setDiffToServer(double ms);
45 | void setDiff(const tv& c2s, const tv& s2c);
46 |
47 | template
48 | inline T getDiffToServer() const
49 | {
50 | return std::chrono::duration_cast(chronos::usec(diffToServer_));
51 | }
52 |
53 | /* chronos::usec::rep getDiffToServer();
54 | chronos::usec::rep getPercentileDiffToServer(size_t percentile);
55 | long getDiffToServerMs();
56 | */
57 |
58 | template
59 | static T sinceEpoche(const chronos::time_point_clk& point)
60 | {
61 | return std::chrono::duration_cast(point.time_since_epoch());
62 | }
63 |
64 | static chronos::time_point_clk toTimePoint(const tv& timeval)
65 | {
66 | return chronos::time_point_clk(chronos::usec(timeval.usec) + chronos::sec(timeval.sec));
67 | }
68 |
69 | inline static chronos::time_point_clk now()
70 | {
71 | return chronos::clk::now();
72 | }
73 |
74 | inline static chronos::time_point_clk serverNow()
75 | {
76 | return chronos::clk::now() + TimeProvider::getInstance().getDiffToServer();
77 | }
78 |
79 | private:
80 | TimeProvider();
81 | TimeProvider(TimeProvider const&); // Don't Implement
82 | void operator=(TimeProvider const&); // Don't implement
83 |
84 | DoubleBuffer diffBuffer_;
85 | std::atomic diffToServer_;
86 | };
87 |
88 |
89 | #endif
90 |
91 |
92 |
--------------------------------------------------------------------------------
/common/daemon.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef DAEMON_H
20 | #define DAEMON_H
21 |
22 | #include
23 |
24 |
25 | class Daemon
26 | {
27 | public:
28 | Daemon(const std::string& user, const std::string& group, const std::string& pidfile);
29 | virtual ~Daemon();
30 |
31 | void daemonize();
32 |
33 | private:
34 | int pidFilehandle_;
35 | std::string user_;
36 | std::string group_;
37 | std::string pidfile_;
38 | };
39 |
40 |
41 | #endif // DAEMON_H
42 |
--------------------------------------------------------------------------------
/common/endian.h:
--------------------------------------------------------------------------------
1 | #ifndef ENDIAN_H
2 | #define ENDIAN_H
3 |
4 | #ifdef IS_BIG_ENDIAN
5 | # define SWAP_16(x) (__builtin_bswap16(x))
6 | # define SWAP_32(x) (__builtin_bswap32(x))
7 | # define SWAP_64(x) (__builtin_bswap64(x))
8 | #else
9 | # define SWAP_16(x) x
10 | # define SWAP_32(x) x
11 | # define SWAP_64(x) x
12 | #endif
13 |
14 | namespace endian
15 | {
16 |
17 | template
18 | T swap(const T&);
19 |
20 | template <>
21 | inline int8_t swap(const int8_t& val)
22 | {
23 | return val;
24 | }
25 |
26 | template <>
27 | inline int16_t swap(const int16_t& val)
28 | {
29 | return SWAP_16(val);
30 | }
31 |
32 | template <>
33 | inline int32_t swap(const int32_t& val)
34 | {
35 | return SWAP_32(val);
36 | }
37 |
38 | template <>
39 | inline int64_t swap(const int64_t& val)
40 | {
41 | return SWAP_64(val);
42 | }
43 |
44 | }
45 |
46 | #endif
47 |
48 |
--------------------------------------------------------------------------------
/common/sampleFormat.cpp:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #include
20 | #include
21 | #include
22 |
23 | #include "sampleFormat.h"
24 | #include "common/strCompat.h"
25 | #include "common/utils/string_utils.h"
26 | #include "common/utils.h"
27 | #include "aixlog.hpp"
28 |
29 |
30 | using namespace std;
31 |
32 |
33 | SampleFormat::SampleFormat()
34 | {
35 | }
36 |
37 |
38 | SampleFormat::SampleFormat(const std::string& format)
39 | {
40 | setFormat(format);
41 | }
42 |
43 |
44 | SampleFormat::SampleFormat(uint32_t sampleRate, uint16_t bitsPerSample, uint16_t channelCount)
45 | {
46 | setFormat(sampleRate, bitsPerSample, channelCount);
47 | }
48 |
49 |
50 | string SampleFormat::getFormat() const
51 | {
52 | stringstream ss;
53 | ss << rate << ":" << bits << ":" << channels;
54 | return ss.str();
55 | }
56 |
57 |
58 | void SampleFormat::setFormat(const std::string& format)
59 | {
60 | std::vector strs;
61 | strs = utils::string::split(format, ':');
62 | if (strs.size() == 3)
63 | setFormat(
64 | cpt::stoul(strs[0]),
65 | cpt::stoul(strs[1]),
66 | cpt::stoul(strs[2]));
67 | }
68 |
69 |
70 | void SampleFormat::setFormat(uint32_t rate, uint16_t bits, uint16_t channels)
71 | {
72 | //needs something like:
73 | // 24_4 = 3 bytes, padded to 4
74 | // 32 = 4 bytes
75 | this->rate = rate;
76 | this->bits = bits;
77 | this->channels = channels;
78 | sampleSize = bits / 8;
79 | if (bits == 24)
80 | sampleSize = 4;
81 | frameSize = channels*sampleSize;
82 | // LOG(DEBUG) << "SampleFormat: " << rate << ":" << bits << ":" << channels << "\n";
83 | }
84 |
85 |
86 |
--------------------------------------------------------------------------------
/common/sampleFormat.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef SAMPLE_FORMAT_H
20 | #define SAMPLE_FORMAT_H
21 |
22 | #include
23 |
24 |
25 | /**
26 | * sample and frame as defined in alsa:
27 | * http://www.alsa-project.org/main/index.php/FramesPeriods
28 | * Say we want to work with a stereo, 16-bit, 44.1 KHz stream, one-way (meaning, either in playback or in capture direction). Then we have:
29 | * 'stereo' = number of channels: 2
30 | * 1 analog sample is represented with 16 bits = 2 bytes
31 | * 1 frame represents 1 analog sample from all channels; here we have 2 channels, and so:
32 | * 1 frame = (num_channels) * (1 sample in bytes) = (2 channels) * (2 bytes (16 bits) per sample) = 4 bytes (32 bits)
33 | * To sustain 2x 44.1 KHz analog rate - the system must be capable of data transfer rate, in Bytes/sec:
34 | * Bps_rate = (num_channels) * (1 sample in bytes) * (analog_rate) = (1 frame) * (analog_rate) = ( 2 channels ) * (2 bytes/sample) * (44100 samples/sec) = 2*2*44100 = 176400 Bytes/sec (link to formula img)
35 | */
36 | class SampleFormat
37 | {
38 | public:
39 | SampleFormat();
40 | SampleFormat(const std::string& format);
41 | SampleFormat(uint32_t rate, uint16_t bits, uint16_t channels);
42 |
43 | std::string getFormat() const;
44 |
45 | void setFormat(const std::string& format);
46 | void setFormat(uint32_t rate, uint16_t bits, uint16_t channels);
47 |
48 | uint32_t rate;
49 | uint16_t bits;
50 | uint16_t channels;
51 |
52 | // size in [bytes] of a single mono sample, e.g. 2 bytes (= 16 bits)
53 | uint16_t sampleSize;
54 |
55 | // size in [bytes] of a frame (sum of sample sizes = #channel*sampleSize), e.g. 4 bytes (= 2 channel * 16 bit)
56 | uint16_t frameSize;
57 |
58 | inline double msRate() const
59 | {
60 | return (double)rate/1000.;
61 | }
62 |
63 | inline double usRate() const
64 | {
65 | return (double)rate/1000000.;
66 | }
67 |
68 | inline double nsRate() const
69 | {
70 | return (double)rate/1000000000.;
71 | }
72 | };
73 |
74 |
75 | #endif
76 |
77 |
--------------------------------------------------------------------------------
/common/signalHandler.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef SIGNAL_HANDLER_H
20 | #define SIGNAL_HANDLER_H
21 |
22 | #include
23 | #include
24 |
25 | extern volatile sig_atomic_t g_terminated;
26 |
27 | void signal_handler(int sig)
28 | {
29 |
30 | switch(sig)
31 | {
32 | case SIGHUP:
33 | syslog(LOG_WARNING, "Received SIGHUP signal.");
34 | break;
35 | case SIGTERM:
36 | syslog(LOG_WARNING, "Received SIGTERM signal.");
37 | g_terminated = true;
38 | break;
39 | case SIGINT:
40 | syslog(LOG_WARNING, "Received SIGINT signal.");
41 | g_terminated = true;
42 | break;
43 | default:
44 | syslog(LOG_WARNING, "Unhandled signal ");
45 | break;
46 | }
47 | }
48 |
49 | #endif
50 |
51 |
52 |
--------------------------------------------------------------------------------
/common/snapException.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef SNAP_EXCEPTION_H
20 | #define SNAP_EXCEPTION_H
21 |
22 | #include
23 | #include
24 | #include // std::strlen, std::strcpy
25 |
26 | // text_exception uses a dynamically-allocated internal c-string for what():
27 | class SnapException : public std::exception {
28 | char* text_;
29 | public:
30 | SnapException(const char* text)
31 | {
32 | text_ = new char[std::strlen(text) + 1];
33 | std::strcpy(text_, text);
34 | }
35 |
36 | SnapException(const std::string& text) : SnapException(text.c_str())
37 | {
38 | }
39 |
40 | SnapException(const SnapException& e) : SnapException(e.what())
41 | {
42 | }
43 |
44 | virtual ~SnapException() throw()
45 | {
46 | delete[] text_;
47 | }
48 |
49 | virtual const char* what() const noexcept
50 | {
51 | return text_;
52 | }
53 | };
54 |
55 |
56 |
57 | class AsyncSnapException : public SnapException
58 | {
59 | public:
60 | AsyncSnapException(const char* text) : SnapException(text)
61 | {
62 | }
63 |
64 | AsyncSnapException(const std::string& text) : SnapException(text)
65 | {
66 | }
67 |
68 | AsyncSnapException(const AsyncSnapException& e) : SnapException(e.what())
69 | {
70 | }
71 |
72 |
73 | virtual ~AsyncSnapException() throw()
74 | {
75 | }
76 | };
77 |
78 |
79 |
80 | #endif
81 |
82 |
83 |
--------------------------------------------------------------------------------
/common/strCompat.h:
--------------------------------------------------------------------------------
1 | #ifndef COMPAT_H
2 | #define COMPAT_H
3 |
4 |
5 | #include
6 |
7 | #ifdef NO_CPP11_STRING
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #endif
15 |
16 |
17 | namespace cpt
18 | {
19 | template
20 | static std::string to_string(const T& t)
21 | {
22 | #ifdef NO_CPP11_STRING
23 | std::stringstream ss;
24 | ss << t;
25 | return ss.str();
26 | #else
27 | return std::to_string(t);
28 | #endif
29 | }
30 |
31 | static long stoul(const std::string& str)
32 | {
33 | #ifdef NO_CPP11_STRING
34 | errno = 0;
35 | char *temp;
36 | long val = strtol(str.c_str(), &temp, 10);
37 | if (temp == str.c_str() || *temp != '\0')
38 | throw std::invalid_argument("stoi");
39 | if (((val == LONG_MIN) || (val == LONG_MAX)) && (errno == ERANGE))
40 | throw std::out_of_range("stoi");
41 | return val;
42 | #else
43 | return std::stoul(str);
44 | #endif
45 | }
46 |
47 | static int stoi(const std::string& str)
48 | {
49 | #ifdef NO_CPP11_STRING
50 | return cpt::stoul(str);
51 | #else
52 | return std::stoi(str);
53 | #endif
54 | }
55 |
56 | static double stod(const std::string& str)
57 | {
58 | #ifdef NO_CPP11_STRING
59 | errno = 0;
60 | char *temp;
61 | double val = strtod(str.c_str(), &temp);
62 | if (temp == str.c_str() || *temp != '\0')
63 | throw std::invalid_argument("stod");
64 | if ((val == HUGE_VAL) && (errno == ERANGE))
65 | throw std::out_of_range("stod");
66 | return val;
67 | #else
68 | return std::stod(str.c_str());
69 | #endif
70 | }
71 |
72 | static long double strtold(const char* str, char** endptr)
73 | {
74 | #ifdef NO_CPP11_STRING
75 | return cpt::stod(str);
76 | #else
77 | return std::strtold(str, endptr);
78 | #endif
79 | }
80 |
81 | static float strtof(const char* str, char** endptr)
82 | {
83 | #ifdef NO_CPP11_STRING
84 | return (float)cpt::stod(str);
85 | #else
86 | return std::strtof(str, endptr);
87 | #endif
88 | }
89 | }
90 |
91 |
92 | #endif
93 |
94 |
--------------------------------------------------------------------------------
/common/utils/file_utils.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef FILE_UTILS_H
20 | #define FILE_UTILS_H
21 |
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include "common/utils/string_utils.h"
28 |
29 |
30 | namespace utils
31 | {
32 | namespace file
33 | {
34 |
35 |
36 | static bool exists(const std::string& filename)
37 | {
38 | std::ifstream infile(filename.c_str());
39 | return infile.good();
40 | }
41 |
42 |
43 | static void do_chown(const std::string& file_path, const std::string& user_name, const std::string& group_name)
44 | {
45 | if (user_name.empty() && group_name.empty())
46 | return;
47 |
48 | if (!exists(file_path))
49 | return;
50 |
51 | uid_t uid = -1;
52 | gid_t gid = -1;
53 |
54 | if (!user_name.empty())
55 | {
56 | struct passwd *pwd = getpwnam(user_name.c_str());
57 | if (pwd == NULL)
58 | throw std::runtime_error("Failed to get uid");
59 | uid = pwd->pw_uid;
60 | }
61 |
62 | if (!group_name.empty())
63 | {
64 | struct group *grp = getgrnam(group_name.c_str());
65 | if (grp == NULL)
66 | throw std::runtime_error("Failed to get gid");
67 | gid = grp->gr_gid;
68 | }
69 |
70 | if (chown(file_path.c_str(), uid, gid) == -1)
71 | throw std::runtime_error("chown failed");
72 | }
73 |
74 |
75 | static int mkdirRecursive(const char *path, mode_t mode)
76 | {
77 | std::vector pathes = utils::string::split(path, '/');
78 | std::stringstream ss;
79 | int res = 0;
80 | for (const auto& p: pathes)
81 | {
82 | if (p.empty())
83 | continue;
84 | ss << "/" << p;
85 | int res = mkdir(ss.str().c_str(), mode);
86 | if ((res != 0) && (errno != EEXIST))
87 | return res;
88 | }
89 | return res;
90 | }
91 |
92 | } // namespace file
93 | } // namespace utils
94 |
95 | #endif
96 |
97 |
--------------------------------------------------------------------------------
/control/control.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import sys
3 | import telnetlib
4 | import json
5 | import threading
6 | import time
7 |
8 | if len(sys.argv) < 3:
9 | print("usage: control.py [setVolume|setName]")
10 | sys.exit(0)
11 |
12 | telnet = telnetlib.Telnet(sys.argv[1], 1705)
13 | requestId = 1
14 |
15 | def doRequest( j, requestId ):
16 | print("send: " + j)
17 | telnet.write(j + "\r\n")
18 | while (True):
19 | response = telnet.read_until("\r\n", 2)
20 | jResponse = json.loads(response)
21 | if 'id' in jResponse:
22 | if jResponse['id'] == requestId:
23 | print("recv: " + response)
24 | return jResponse;
25 | return;
26 |
27 | def setVolume(client, volume):
28 | global requestId
29 | doRequest(json.dumps({'jsonrpc': '2.0', 'method': 'Client.SetVolume', 'params': {'client': client, 'volume': volume}, 'id': requestId}), requestId)
30 | requestId = requestId + 1
31 |
32 | def setName(client, name):
33 | global requestId
34 | doRequest(json.dumps({'jsonrpc': '2.0', 'method': 'Client.SetName', 'params': {'client': client, 'name': name}, 'id': requestId}), requestId)
35 | requestId = requestId + 1
36 |
37 | if sys.argv[2] == "setVolume":
38 | if len(sys.argv) < 5:
39 | print("usage: control.py setVolume [+/-]")
40 | exit(0)
41 | volstr = sys.argv[4]
42 | j = doRequest(json.dumps({'jsonrpc': '2.0', 'method': 'Server.GetStatus', 'id': 1}), 1)
43 | for client in j["result"]["clients"]:
44 | if(sys.argv[3] == client['host']['name'] or sys.argv[3] == 'all'):
45 | if(volstr[0] == '+'):
46 | volume = int(client['config']['volume']['percent']) + int(volstr[1:])
47 | elif(volstr[0] == '-'):
48 | volume = int(client['config']['volume']['percent']) - int(volstr[1:])
49 | else:
50 | volume = int(volstr)
51 | setVolume(client['host']['mac'], volume)
52 |
53 | elif sys.argv[2] == "setName":
54 | if len(sys.argv) < 5:
55 | print("usage: control.py setName ")
56 | exit(0)
57 | setName(sys.argv[3], sys.argv[4])
58 |
59 | else:
60 | print("unknown command \"" + sys.argv[2] + "\"")
61 |
62 | j = doRequest(json.dumps({'jsonrpc': '2.0', 'method': 'Server.GetStatus', 'id': 1}), 1)
63 | for client in j["result"]["clients"]:
64 | print("MAC: " + client['host']['mac'] + ", connect: " + str(client['connected']) + ", volume: " + str(client['config']['volume']['percent']) + ", name: " + client['host']['name'] + ", host: " + client['host']['ip'])
65 |
66 | telnet.close
67 |
68 |
--------------------------------------------------------------------------------
/control/setVolume.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import sys
3 | import telnetlib
4 | import json
5 | import threading
6 | import time
7 |
8 | telnet = telnetlib.Telnet(sys.argv[1], 1705)
9 | requestId = 1
10 |
11 | def doRequest( j, requestId ):
12 | print("send: " + j)
13 | telnet.write(j + "\r\n")
14 | while (True):
15 | response = telnet.read_until("\r\n", 2)
16 | jResponse = json.loads(response)
17 | if 'id' in jResponse:
18 | if jResponse['id'] == requestId:
19 | # print("recv: " + response)
20 | return jResponse;
21 | return;
22 |
23 | def setVolume(client, volume):
24 | global requestId
25 | doRequest(json.dumps({'jsonrpc': '2.0', 'method': 'Client.SetVolume', 'params': {'id': client, 'volume': {'muted': False, 'percent': volume}}, 'id': requestId}), requestId)
26 | requestId = requestId + 1
27 |
28 | volume = int(sys.argv[2])
29 | j = doRequest(json.dumps({'jsonrpc': '2.0', 'method': 'Server.GetStatus', 'id': 1}), 1)
30 | for group in j["result"]["server"]["groups"]:
31 | for client in group["clients"]:
32 | setVolume(client['id'], volume)
33 |
34 | j = doRequest(json.dumps({'jsonrpc': '2.0', 'method': 'Server.GetStatus', 'id': 1}), 1)
35 | for group in j["result"]["server"]["groups"]:
36 | for client in group["clients"]:
37 | print("MAC: " + client['host']['mac'] + ", name: " + client['config']['name'] + ", conntect: " + str(client['connected']) + ", volume: " + str(client['config']['volume']['percent']))
38 |
39 | telnet.close
40 |
41 |
--------------------------------------------------------------------------------
/doc/Snapcast.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/doc/Snapcast.png
--------------------------------------------------------------------------------
/doc/Snapcast_800.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/doc/Snapcast_800.png
--------------------------------------------------------------------------------
/doc/snapcast_android.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/doc/snapcast_android.png
--------------------------------------------------------------------------------
/doc/snapcast_android_scaled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/doc/snapcast_android_scaled.png
--------------------------------------------------------------------------------
/doc/web_hi_res_512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mjaggard/snapcast/d878983a1a6b2bfa7cb92dd7cc05070bb4c7540a/doc/web_hi_res_512.png
--------------------------------------------------------------------------------
/docs/PRIVACY.md:
--------------------------------------------------------------------------------
1 | Privacy Policy
2 | ==============
3 |
4 | Snapcast does not currently collect any user information for any purpose.
5 |
6 | It does however embed AdMob which performs analytics based on your preferences on [Google Ads Settings](https://www.google.co.uk/settings/ads)
7 |
8 | For more detailed information, please see
9 | [https://support.google.com/admob/answer/6128543?hl=en-GB](https://support.google.com/admob/answer/6128543?hl=en-GB)
10 |
--------------------------------------------------------------------------------
/docs/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-slate
--------------------------------------------------------------------------------
/message/codecHeader.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef CODEC_HEADER_MESSAGE_H
20 | #define CODEC_HEADER_MESSAGE_H
21 |
22 | #include "message.h"
23 |
24 | namespace msg
25 | {
26 |
27 | /**
28 | * Codec dependend header of encoded data stream
29 | */
30 | class CodecHeader : public BaseMessage
31 | {
32 | public:
33 | CodecHeader(const std::string& codecName = "", size_t size = 0) : BaseMessage(message_type::kCodecHeader), payloadSize(size), codec(codecName)
34 | {
35 | payload = (char*)malloc(size);
36 | }
37 |
38 | virtual ~CodecHeader()
39 | {
40 | free(payload);
41 | }
42 |
43 | virtual void read(std::istream& stream)
44 | {
45 | readVal(stream, codec);
46 | readVal(stream, &payload, payloadSize);
47 | }
48 |
49 | virtual uint32_t getSize() const
50 | {
51 | return sizeof(uint32_t) + codec.size() + sizeof(uint32_t) + payloadSize;
52 | }
53 |
54 | uint32_t payloadSize;
55 | char* payload;
56 | std::string codec;
57 |
58 | protected:
59 | virtual void doserialize(std::ostream& stream) const
60 | {
61 | writeVal(stream, codec);
62 | writeVal(stream, payload, payloadSize);
63 | }
64 | };
65 |
66 | }
67 |
68 |
69 | #endif
70 |
71 |
72 |
--------------------------------------------------------------------------------
/message/hello.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef HELLO_MSG_H
20 | #define HELLO_MSG_H
21 |
22 | #include "jsonMessage.h"
23 | #include "common/utils.h"
24 | #include "common/strCompat.h"
25 | #include
26 |
27 |
28 | namespace msg
29 | {
30 |
31 | class Hello : public JsonMessage
32 | {
33 | public:
34 | Hello() : JsonMessage(message_type::kHello)
35 | {
36 | }
37 |
38 | Hello(const std::string& macAddress, const std::string& id, size_t instance) : JsonMessage(message_type::kHello)
39 | {
40 | msg["MAC"] = macAddress;
41 | msg["HostName"] = ::getHostName();
42 | msg["Version"] = VERSION;
43 | msg["ClientName"] = "Snapclient";
44 | msg["OS"] = ::getOS();
45 | msg["Arch"] = ::getArch();
46 | msg["Instance"] = instance;
47 | msg["ID"] = id;
48 | msg["SnapStreamProtocolVersion"] = 2;
49 | }
50 |
51 | virtual ~Hello()
52 | {
53 | }
54 |
55 | std::string getMacAddress() const
56 | {
57 | return msg["MAC"];
58 | }
59 |
60 | std::string getHostName() const
61 | {
62 | return msg["HostName"];
63 | }
64 |
65 | std::string getVersion() const
66 | {
67 | return msg["Version"];
68 | }
69 |
70 | std::string getClientName() const
71 | {
72 | return msg["ClientName"];
73 | }
74 |
75 | std::string getOS() const
76 | {
77 | return msg["OS"];
78 | }
79 |
80 | std::string getArch() const
81 | {
82 | return msg["Arch"];
83 | }
84 |
85 | int getInstance() const
86 | {
87 | return get("Instance", 1);
88 | }
89 |
90 | int getProtocolVersion() const
91 | {
92 | return get("SnapStreamProtocolVersion", 1);
93 | }
94 |
95 | std::string getId() const
96 | {
97 | return get("ID", getMacAddress());
98 | }
99 |
100 | std::string getUniqueId() const
101 | {
102 | std::string id = getId();
103 | int instance = getInstance();
104 | if (instance != 1)
105 | {
106 | id = id + "#" + cpt::to_string(instance);
107 | }
108 | return id;
109 | }
110 | };
111 |
112 | }
113 |
114 |
115 | #endif
116 |
117 |
118 |
--------------------------------------------------------------------------------
/message/jsonMessage.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef JSON_MESSAGE_H
20 | #define JSON_MESSAGE_H
21 |
22 | #include "message.h"
23 | #include "externals/json.hpp"
24 |
25 |
26 | using json = nlohmann::json;
27 |
28 |
29 | namespace msg
30 | {
31 |
32 | class JsonMessage : public BaseMessage
33 | {
34 | public:
35 | JsonMessage(message_type msgType) : BaseMessage(msgType)
36 | {
37 | }
38 |
39 | virtual ~JsonMessage()
40 | {
41 | }
42 |
43 | virtual void read(std::istream& stream)
44 | {
45 | std::string s;
46 | readVal(stream, s);
47 | msg = json::parse(s);
48 | }
49 |
50 | virtual uint32_t getSize() const
51 | {
52 | return sizeof(uint32_t) + msg.dump().size();
53 | }
54 |
55 | json msg;
56 |
57 |
58 | protected:
59 | virtual void doserialize(std::ostream& stream) const
60 | {
61 | writeVal(stream, msg.dump());
62 | }
63 |
64 | template
65 | T get(const std::string& what, const T& def) const
66 | {
67 | try
68 | {
69 | if (!msg.count(what))
70 | return def;
71 | return msg[what].get();
72 | }
73 | catch(...)
74 | {
75 | return def;
76 | }
77 | }
78 | };
79 |
80 | }
81 |
82 |
83 | #endif
84 |
85 |
86 |
--------------------------------------------------------------------------------
/message/pcmChunk.cpp:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #include
20 | #include
21 | #include "pcmChunk.h"
22 | #include "aixlog.hpp"
23 |
24 |
25 | using namespace std;
26 |
27 | namespace msg
28 | {
29 |
30 | PcmChunk::PcmChunk(const SampleFormat& sampleFormat, size_t ms) : WireChunk(sampleFormat.rate*sampleFormat.frameSize*ms / 1000), format(sampleFormat), idx_(0)
31 | {
32 | }
33 |
34 |
35 | PcmChunk::PcmChunk(const PcmChunk& pcmChunk) : WireChunk(pcmChunk), format(pcmChunk.format), idx_(0)
36 | {
37 | }
38 |
39 |
40 | PcmChunk::PcmChunk() : WireChunk(), idx_(0)
41 | {
42 | }
43 |
44 |
45 | PcmChunk::~PcmChunk()
46 | {
47 | }
48 |
49 |
50 |
51 | int PcmChunk::seek(int frames)
52 | {
53 | if ((frames < 0) && (-frames > (int)idx_))
54 | frames = -idx_;
55 |
56 | idx_ += frames;
57 | if (idx_ > getFrameCount())
58 | idx_ = getFrameCount();
59 |
60 | return idx_;
61 | }
62 |
63 |
64 | int PcmChunk::readFrames(void* outputBuffer, size_t frameCount)
65 | {
66 | //logd << "read: " << frameCount << ", total: " << (wireChunk->length / format.frameSize) << ", idx: " << idx;// << std::endl;
67 | int result = frameCount;
68 | if (idx_ + frameCount > (payloadSize / format.frameSize))
69 | result = (payloadSize / format.frameSize) - idx_;
70 |
71 | //logd << ", from: " << format.frameSize*idx << ", to: " << format.frameSize*idx + format.frameSize*result;
72 | if (outputBuffer != NULL)
73 | memcpy((char*)outputBuffer, (char*)(payload) + format.frameSize*idx_, format.frameSize*result);
74 |
75 | idx_ += result;
76 | //logd << ", new idx: " << idx << ", result: " << result << ", wireChunk->length: " << wireChunk->length << ", format.frameSize: " << format.frameSize << "\n";//std::endl;
77 | return result;
78 | }
79 |
80 |
81 | }
82 |
83 |
84 |
--------------------------------------------------------------------------------
/message/pcmChunk.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef PCM_CHUNK_H
20 | #define PCM_CHUNK_H
21 |
22 | #include
23 | #include "message.h"
24 | #include "wireChunk.h"
25 | #include "common/sampleFormat.h"
26 |
27 |
28 | namespace msg
29 | {
30 |
31 | /**
32 | * Piece of PCM data with SampleFormat information
33 | * Has information about "when" recorded (start) and duration
34 | * frames can be read with "readFrames", which will also change the start time
35 | */
36 | class PcmChunk : public WireChunk
37 | {
38 | public:
39 | PcmChunk(const SampleFormat& sampleFormat, size_t ms);
40 | PcmChunk(const PcmChunk& pcmChunk);
41 | PcmChunk();
42 | virtual ~PcmChunk();
43 |
44 | int readFrames(void* outputBuffer, size_t frameCount);
45 | int seek(int frames);
46 |
47 | virtual chronos::time_point_clk start() const
48 | {
49 | return chronos::time_point_clk(
50 | chronos::sec(timestamp.sec) +
51 | chronos::usec(timestamp.usec) +
52 | chronos::usec((chronos::usec::rep)(1000000. * ((double)idx_ / (double)format.rate)))
53 | );
54 | }
55 |
56 | inline chronos::time_point_clk end() const
57 | {
58 | return start() + durationLeft();
59 | }
60 |
61 | template
62 | inline T duration() const
63 | {
64 | return std::chrono::duration_cast(chronos::nsec((chronos::nsec::rep)(1000000 * getFrameCount() / format.msRate())));
65 | }
66 |
67 | template
68 | inline T durationLeft() const
69 | {
70 | return std::chrono::duration_cast(chronos::nsec((chronos::nsec::rep)(1000000 * (getFrameCount() - idx_) / format.msRate())));
71 | }
72 |
73 | inline bool isEndOfChunk() const
74 | {
75 | return idx_ >= getFrameCount();
76 | }
77 |
78 | inline size_t getFrameCount() const
79 | {
80 | return (payloadSize / format.frameSize);
81 | }
82 |
83 | inline size_t getSampleCount() const
84 | {
85 | return (payloadSize / format.sampleSize);
86 | }
87 |
88 | SampleFormat format;
89 |
90 | private:
91 | uint32_t idx_;
92 | };
93 |
94 | }
95 |
96 | #endif
97 |
98 |
99 |
--------------------------------------------------------------------------------
/message/serverSettings.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef SERVER_SETTINGS_H
20 | #define SERVER_SETTINGS_H
21 |
22 | #include "jsonMessage.h"
23 |
24 |
25 | namespace msg
26 | {
27 |
28 | class ServerSettings : public JsonMessage
29 | {
30 | public:
31 | ServerSettings() : JsonMessage(message_type::kServerSettings)
32 | {
33 | setBufferMs(0);
34 | setLatency(0);
35 | setVolume(100);
36 | setMuted(false);
37 | }
38 |
39 | virtual ~ServerSettings()
40 | {
41 | }
42 |
43 | int32_t getBufferMs()
44 | {
45 | return get("bufferMs", 0);
46 | }
47 |
48 | int32_t getLatency()
49 | {
50 | return get("latency", 0);
51 | }
52 |
53 | uint16_t getVolume()
54 | {
55 | return get("volume", 100);
56 | }
57 |
58 | bool isMuted()
59 | {
60 | return get("muted", false);
61 | }
62 |
63 |
64 |
65 | void setBufferMs(int32_t bufferMs)
66 | {
67 | msg["bufferMs"] = bufferMs;
68 | }
69 |
70 | void setLatency(int32_t latency)
71 | {
72 | msg["latency"] = latency;
73 | }
74 |
75 | void setVolume(uint16_t volume)
76 | {
77 | msg["volume"] = volume;
78 | }
79 |
80 | void setMuted(bool muted)
81 | {
82 | msg["muted"] = muted;
83 | }
84 | };
85 |
86 | }
87 |
88 |
89 | #endif
90 |
91 |
92 |
--------------------------------------------------------------------------------
/message/time.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef TIME_MSG_H
20 | #define TIME_MSG_H
21 |
22 | #include "message.h"
23 |
24 | namespace msg
25 | {
26 |
27 | class Time : public BaseMessage
28 | {
29 | public:
30 | Time() : BaseMessage(message_type::kTime)
31 | {
32 | }
33 |
34 | virtual ~Time()
35 | {
36 | }
37 |
38 | virtual void read(std::istream& stream)
39 | {
40 | readVal(stream, latency.sec);
41 | readVal(stream, latency.usec);
42 | }
43 |
44 | virtual uint32_t getSize() const
45 | {
46 | return sizeof(tv);
47 | }
48 |
49 | tv latency;
50 |
51 | protected:
52 | virtual void doserialize(std::ostream& stream) const
53 | {
54 | writeVal(stream, latency.sec);
55 | writeVal(stream, latency.usec);
56 | }
57 | };
58 |
59 | }
60 |
61 |
62 | #endif
63 |
64 |
65 |
--------------------------------------------------------------------------------
/message/wireChunk.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef WIRE_CHUNK_H
20 | #define WIRE_CHUNK_H
21 |
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include "message.h"
29 | #include "common/timeDefs.h"
30 |
31 |
32 | namespace msg
33 | {
34 |
35 | /**
36 | * Piece of raw data
37 | * Has information about "when" captured (timestamp)
38 | */
39 | class WireChunk : public BaseMessage
40 | {
41 | public:
42 | WireChunk(size_t size = 0) : BaseMessage(message_type::kWireChunk), payloadSize(size)
43 | {
44 | payload = (char*)malloc(size);
45 | }
46 |
47 | WireChunk(const WireChunk& wireChunk) : BaseMessage(message_type::kWireChunk), timestamp(wireChunk.timestamp), payloadSize(wireChunk.payloadSize)
48 | {
49 | payload = (char*)malloc(payloadSize);
50 | memcpy(payload, wireChunk.payload, payloadSize);
51 | }
52 |
53 | virtual ~WireChunk()
54 | {
55 | free(payload);
56 | }
57 |
58 | virtual void read(std::istream& stream)
59 | {
60 | readVal(stream, timestamp.sec);
61 | readVal(stream, timestamp.usec);
62 | readVal(stream, &payload, payloadSize);
63 | }
64 |
65 | virtual uint32_t getSize() const
66 | {
67 | return sizeof(tv) + sizeof(int32_t) + payloadSize;
68 | }
69 |
70 | virtual chronos::time_point_clk start() const
71 | {
72 | return chronos::time_point_clk(chronos::sec(timestamp.sec) + chronos::usec(timestamp.usec));
73 | }
74 |
75 | tv timestamp;
76 | uint32_t payloadSize;
77 | char* payload;
78 |
79 | protected:
80 | virtual void doserialize(std::ostream& stream) const
81 | {
82 | writeVal(stream, timestamp.sec);
83 | writeVal(stream, timestamp.usec);
84 | writeVal(stream, payload, payloadSize);
85 | }
86 | };
87 |
88 | }
89 |
90 |
91 | #endif
92 |
93 |
94 |
--------------------------------------------------------------------------------
/openWrt/snapclient.init:
--------------------------------------------------------------------------------
1 | #!/bin/sh /etc/rc.common
2 | # Author: Johannes Pohl
3 | START=90
4 |
5 | SERVICE_NAME=snapclient
6 | SERVICE_WRITE_PID=1
7 | SERVICE_DAEMONIZE=1
8 | SERVICE_PID_FILE=/var/run/$SERVICE_NAME/pid
9 |
10 | DESC="Snapcast client"
11 | DAEMON=/usr/bin/$SERVICE_NAME
12 |
13 |
14 | # Exit if the package is not installed
15 | [ -x "$DAEMON" ] || exit 0
16 |
17 | # Read configuration variable file if it is present
18 | [ -r /etc/default/$SERVICE_NAME ] && . /etc/default/$SERVICE_NAME
19 | SNAPCLIENT_OPTS="-d $SNAPCLIENT_OPTS"
20 |
21 | if [ "$START_SNAPCLIENT" != "true" ] ; then
22 | exit 0
23 | fi
24 |
25 | #
26 | # Function that starts the daemon/service
27 | #
28 | start()
29 | {
30 | service_start $DAEMON $SNAPCLIENT_OPTS
31 | }
32 |
33 | #
34 | # Function that stops the daemon/service
35 | #
36 | stop()
37 | {
38 | service_stop $DAEMON
39 | #killall $SERVICE_NAME
40 | # Many daemons don't delete their pidfiles when they exit.
41 | rm -f $SERVICE_PID_FILE
42 | }
43 |
44 |
--------------------------------------------------------------------------------
/openWrt/snapserver.init:
--------------------------------------------------------------------------------
1 | #!/bin/sh /etc/rc.common
2 | # Author: Johannes Pohl
3 | START=90
4 |
5 | SERVICE_NAME=snapserver
6 | SERVICE_WRITE_PID=1
7 | SERVICE_DAEMONIZE=1
8 | SERVICE_PID_FILE=/var/run/$SERVICE_NAME/pid
9 |
10 | DESC="Snapcast server"
11 | DAEMON=/usr/bin/$SERVICE_NAME
12 |
13 |
14 | # Exit if the package is not installed
15 | [ -x "$DAEMON" ] || exit 0
16 |
17 | # Read configuration variable file if it is present
18 | [ -r /etc/default/$SERVICE_NAME ] && . /etc/default/$SERVICE_NAME
19 | SNAPSERVER_OPTS="-d $SNAPSERVER_OPTS"
20 |
21 | if [ "$START_SNAPSERVER" != "true" ] ; then
22 | exit 0
23 | fi
24 |
25 | #
26 | # Function that starts the daemon/service
27 | #
28 | start()
29 | {
30 | service_start $DAEMON $SNAPSERVER_OPTS
31 | }
32 |
33 | #
34 | # Function that stops the daemon/service
35 | #
36 | stop()
37 | {
38 | service_stop $DAEMON
39 | #killall $SERVICE_NAME
40 | # Many daemons don't delete their pidfiles when they exit.
41 | rm -f $SERVICE_PID_FILE
42 | }
43 |
44 |
--------------------------------------------------------------------------------
/server/build_android.sh:
--------------------------------------------------------------------------------
1 | #/bin/sh
2 |
3 | if [ -z "$NDK_DIR_ARM" ] && [ -z "$NDK_DIR_ARM64" ] && [ -z "$NDK_DIR_MIPS" ] && [ -z "$NDK_DIR_X86" ]; then
4 | echo "Specify at least one NDK_DIR_[ARM|ARM64|MIPS|X86]"
5 | exit
6 | fi
7 |
8 | if [ -n "$NDK_DIR_ARM" ]; then
9 | export NDK_DIR="$NDK_DIR_ARM"
10 | export ARCH=arm
11 | make clean; make TARGET=ANDROID -j 4; mv ./snapserver ../android/Snapcast/src/main/assets/bin/armeabi/
12 | fi
13 |
14 | if [ -n "$NDK_DIR_ARM64" ]; then
15 | export NDK_DIR="$NDK_DIR_ARM64"
16 | export ARCH=aarch64
17 | make clean; make TARGET=ANDROID -j 4; mv ./snapserver ../android/Snapcast/src/main/assets/bin/arm64-v8a/
18 | fi
19 |
20 | if [ -n "$NDK_DIR_MIPS" ]; then
21 | export NDK_DIR="$NDK_DIR_MIPS"
22 | export ARCH=mips
23 | make clean; make TARGET=ANDROID -j 4; mv ./snapserver ../android/Snapcast/src/main/assets/bin/mips/
24 | fi
25 |
26 | if [ -n "$NDK_DIR_X86" ]; then
27 | export NDK_DIR="$NDK_DIR_X86"
28 | export ARCH=x86
29 | make clean; make TARGET=ANDROID -j 4; mv ./snapserver ../android/Snapcast/src/main/assets/bin/x86/
30 | fi
31 |
--------------------------------------------------------------------------------
/server/build_android_all.sh:
--------------------------------------------------------------------------------
1 | #/bin/sh
2 |
3 | export NDK_DIR_ARM="$1-arm"
4 | export NDK_DIR_ARM64="$1-aarch64"
5 | export NDK_DIR_MIPS="$1-mips"
6 | export NDK_DIR_X86="$1-x86"
7 |
8 | ./build_android.sh
9 |
--------------------------------------------------------------------------------
/server/controlServer.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef CONTROL_SERVER_H
20 | #define CONTROL_SERVER_H
21 |
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 |
30 | #include "controlSession.h"
31 | #include "common/queue.h"
32 | #include "common/sampleFormat.h"
33 | #include "message/message.h"
34 | #include "message/codecHeader.h"
35 | #include "message/serverSettings.h"
36 |
37 |
38 | using asio::ip::tcp;
39 | typedef std::shared_ptr socket_ptr;
40 |
41 |
42 | /// Telnet like remote control
43 | /**
44 | * Telnet like remote control
45 | */
46 | class ControlServer : public ControlMessageReceiver
47 | {
48 | public:
49 | ControlServer(asio::io_service* io_service, size_t port, ControlMessageReceiver* controlMessageReceiver = NULL);
50 | virtual ~ControlServer();
51 |
52 | void start();
53 | void stop();
54 |
55 | /// Send a message to all connceted clients
56 | void send(const std::string& message, const ControlSession* excludeSession = NULL);
57 |
58 | /// Clients call this when they receive a message. Implementation of MessageReceiver::onMessageReceived
59 | virtual void onMessageReceived(ControlSession* connection, const std::string& message);
60 |
61 | private:
62 | void startAccept();
63 | void handleAccept(socket_ptr socket);
64 | void cleanup();
65 | // void acceptor();
66 | mutable std::recursive_mutex mutex_;
67 | std::set> sessions_;
68 | std::shared_ptr acceptor_;
69 |
70 | Queue> messages_;
71 | asio::io_service* io_service_;
72 | size_t port_;
73 | ControlMessageReceiver* controlMessageReceiver_;
74 | };
75 |
76 |
77 |
78 | #endif
79 |
80 |
81 |
--------------------------------------------------------------------------------
/server/controlSession.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef CONTROL_SESSION_H
20 | #define CONTROL_SESSION_H
21 |
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include "message/message.h"
31 | #include "common/queue.h"
32 |
33 |
34 | using asio::ip::tcp;
35 |
36 |
37 | class ControlSession;
38 |
39 |
40 | /// Interface: callback for a received message.
41 | class ControlMessageReceiver
42 | {
43 | public:
44 | virtual void onMessageReceived(ControlSession* connection, const std::string& message) = 0;
45 | };
46 |
47 |
48 | /// Endpoint for a connected control client.
49 | /**
50 | * Endpoint for a connected control client.
51 | * Messages are sent to the client with the "send" method.
52 | * Received messages from the client are passed to the ControlMessageReceiver callback
53 | */
54 | class ControlSession
55 | {
56 | public:
57 | /// ctor. Received message from the client are passed to MessageReceiver
58 | ControlSession(ControlMessageReceiver* receiver, std::shared_ptr socket);
59 | ~ControlSession();
60 | void start();
61 | void stop();
62 |
63 | /// Sends a message to the client (synchronous)
64 | bool send(const std::string& message) const;
65 |
66 | /// Sends a message to the client (asynchronous)
67 | void sendAsync(const std::string& message);
68 |
69 | bool active() const
70 | {
71 | return active_;
72 | }
73 |
74 | protected:
75 | void reader();
76 | void writer();
77 |
78 | std::atomic active_;
79 | mutable std::recursive_mutex activeMutex_;
80 | mutable std::recursive_mutex socketMutex_;
81 | std::thread readerThread_;
82 | std::thread writerThread_;
83 | std::shared_ptr socket_;
84 | ControlMessageReceiver* messageReceiver_;
85 | Queue messages_;
86 | };
87 |
88 |
89 |
90 |
91 | #endif
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/server/debian/compat:
--------------------------------------------------------------------------------
1 | 9
2 |
--------------------------------------------------------------------------------
/server/debian/control:
--------------------------------------------------------------------------------
1 | Source: snapserver
2 | Section: utils
3 | Priority: extra
4 | Maintainer: Johannes Pohl
5 | Build-Depends: debhelper (>= 9.0.0), libc6-dev, dh-systemd, libavahi-client3 (>= 0.6.16), libflac8 (>= 1.3.0), libogg0 (>= 1.0rc3), libvorbis0a (>= 1.1.2), libvorbisenc2 (>= 1.1.2)
6 | Standards-Version: 3.8.4
7 | Homepage: https://github.com/badaix/snapcast
8 |
9 | Package: snapserver
10 | Architecture: any
11 | Depends: adduser, ${shlibs:Depends}, ${misc:Depends}
12 | Description: Snapcast server
13 | Snapcast is a multi-room client-server audio player, where all clients are
14 | time synchronized with the server to play perfectly synced audio. It's not a
15 | standalone player, but an extension that turns your existing audio player into
16 | a Sonos-like multi-room solution.
17 | .
18 | The server's audio input is a named pipe `/tmp/snapfifo`. All data that is fed
19 | into this file will be send to the connected clients. One of the most generic
20 | ways to use Snapcast is in conjunction with the music player daemon or Mopidy,
21 | which can be configured to use a named pipe as audio output.
22 |
--------------------------------------------------------------------------------
/server/debian/copyright:
--------------------------------------------------------------------------------
1 | This work was written and packaged by Johannes Pohl
2 |
3 | It was downloaded from:
4 |
5 | Upstream Author: Johannes Pohl
6 |
7 | Copyright:
8 |
9 |
10 |
11 | License:
12 |
13 | This program is free software: you can redistribute it and/or modify
14 | it under the terms of the GNU General Public License as published by
15 | the Free Software Foundation, either version 3 of the License, or
16 | (at your option) any later version.
17 |
18 | This package is distributed in the hope that it will be useful,
19 | but WITHOUT ANY WARRANTY; without even the implied warranty of
20 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 | GNU General Public License for more details.
22 |
23 | You should have received a copy of the GNU General Public License
24 | along with this program. If not, see .
25 |
26 | On Debian systems, the complete text of the GNU General
27 | Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
28 |
29 | The Debian packaging is:
30 |
31 | Copyright (C) 2014-2017 Johannes Pohl
32 |
33 | and is licensed under the GPL version 3, see above.
34 |
35 |
--------------------------------------------------------------------------------
/server/debian/docs:
--------------------------------------------------------------------------------
1 | ../README.md
2 |
--------------------------------------------------------------------------------
/server/debian/rules:
--------------------------------------------------------------------------------
1 | #!/usr/bin/make -f
2 | # -*- makefile -*-
3 | # Sample debian/rules that uses debhelper.
4 | # This file was originally written by Joey Hess and Craig Small.
5 | # As a special exception, when this file is copied by dh-make into a
6 | # dh-make output file, you may use that output file without restriction.
7 | # This special exception was added by Craig Small in version 0.37 of dh-make.
8 |
9 | # Uncomment this to turn on verbose mode.
10 | # export DH_VERBOSE=1
11 |
12 | %:
13 | dh $@ --with=systemd
14 |
--------------------------------------------------------------------------------
/server/debian/snapserver.bsd:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # PROVIDE: snapserver
4 | # REQUIRE: DAEMON
5 | # KEYWORD: Snapserver
6 | #
7 | # Snapserver - Snapcast server
8 | #
9 | # Add the following line to /etc/rc.conf to enable snapserver:
10 | # snapserver_enable=YES
11 | # Add snapserver_opts= to configure command line arguments
12 |
13 | snapserver_opts="-d"
14 |
15 | . /etc/rc.subr
16 |
17 | name=snapserver
18 | rcvar=snapserver_enable
19 | desc="Snapserver - Snapcast server"
20 |
21 | load_rc_config $name
22 |
23 | : ${snapserver_enable:=NO}
24 |
25 | command=/usr/local/bin/${name}
26 | pidfile="/var/run/${name}/pid"
27 |
28 | start_cmd=snapserver_start
29 |
30 |
31 | snapserver_start() {
32 | checkyesno snapserver_enable && echo "Starting snapserver." && ${command} ${snapserver_opts}
33 | }
34 |
35 | run_rc_command $1
36 |
37 |
--------------------------------------------------------------------------------
/server/debian/snapserver.default:
--------------------------------------------------------------------------------
1 | # defaults file for snapserver
2 |
3 | # start snapserver automatically?
4 | START_SNAPSERVER=true
5 |
6 | # Allowed options:
7 | # -h, --help Produce help message
8 | # -v, --version Show version number
9 | # -p, --port arg (=1704) Server port
10 | # --controlPort arg (=1705) Remote control port
11 | # -s, --stream arg (=pipe:///tmp/snapfifo?name=default)
12 | # URI of the PCM input stream.
13 | # Format: TYPE://host/path?name=NAME
14 | # [&codec=CODEC]
15 | # [&sampleformat=SAMPLEFORMAT]
16 | # --sampleformat arg (=48000:16:2) Default sample format
17 | # -c, --codec arg (=flac) Default transport codec
18 | # (flac|ogg|pcm)[:options]
19 | # Type codec:? to get codec specific options
20 | # --streamBuffer arg (=20) Default stream read buffer [ms]
21 | # -b, --buffer arg (=1000) Buffer [ms]
22 | # -d, --daemon [=arg(=0)] Daemonize
23 | # optional process priority [-20..19]
24 | # --user arg the user[:group] to run snapserver as when daemonized
25 |
26 | USER_OPTS="--user snapserver:snapserver"
27 |
28 | SNAPSERVER_OPTS=""
29 |
--------------------------------------------------------------------------------
/server/debian/snapserver.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Label
6 | de.badaix.snapcast.snapserver
7 | ProgramArguments
8 |
9 | /usr/local/bin/snapserver
10 |
11 |
12 | RunAtLoad
13 |
14 | KeepAlive
15 |
16 | ProcessType
17 | Interactive
18 |
19 |
20 |
--------------------------------------------------------------------------------
/server/debian/snapserver.postinst:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | umask 0022
6 | ACTION="$1"
7 | VERSION="$2"
8 |
9 | if [ "$ACTION" = "configure" ]; then
10 | # add the snapserver user
11 | if ! getent passwd snapserver >/dev/null; then
12 | useradd --user-group --system --home-dir /var/lib/snapserver snapserver
13 | fi
14 | fi
15 |
16 | #DEBHELPER#
17 |
--------------------------------------------------------------------------------
/server/debian/snapserver.postrm:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | if [ "$1" = "purge" ]; then
6 | # clean up all statoverrides that belonged to snapserver, so the
7 | # snapserver system user can be removed cleanly
8 | for i in $(dpkg-statoverride --list | awk '/^snapserver / {print $4}'); do
9 | if dpkg-statoverride --list --quiet "$i" >/dev/null; then
10 | dpkg-statoverride --force --quiet --remove "$i"
11 | fi
12 | done
13 | fi
14 |
15 | userdel --force snapserver > /dev/null || true
16 | groupdel snapserver > /dev/null || true
17 |
18 | #DEBHELPER#
19 |
--------------------------------------------------------------------------------
/server/debian/snapserver.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Snapcast server
3 | After=network-online.target
4 | Requires=network-online.target
5 |
6 | [Service]
7 | EnvironmentFile=-/etc/default/snapserver
8 | Type=forking
9 | ExecStart=/usr/bin/snapserver -d $USER_OPTS $SNAPSERVER_OPTS
10 | PIDFile=/var/run/snapserver/pid
11 | Restart=always
12 |
13 | [Install]
14 | WantedBy=multi-user.target
15 |
--------------------------------------------------------------------------------
/server/encoder/encoder.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef ENCODER_H
20 | #define ENCODER_H
21 |
22 | #include
23 | #include
24 |
25 | #include "message/pcmChunk.h"
26 | #include "message/codecHeader.h"
27 | #include "common/sampleFormat.h"
28 |
29 |
30 | class Encoder;
31 |
32 | /// Callback interface for users of Encoder
33 | /**
34 | * Users of Encoder should implement this to get the encoded PCM data
35 | */
36 | class EncoderListener
37 | {
38 | public:
39 | virtual void onChunkEncoded(const Encoder* encoder, msg::PcmChunk* chunk, double duration) = 0;
40 | };
41 |
42 |
43 |
44 | /// Abstract Encoder class
45 | /**
46 | * Stream encoder. PCM chunks are fed into the encoder.
47 | * As soon as a frame is encoded, the encoded data is passed to the EncoderListener
48 | */
49 | class Encoder
50 | {
51 | public:
52 | /// ctor. Codec options (E.g. compression level) are passed as string and are codec dependend
53 | Encoder(const std::string& codecOptions = "") : headerChunk_(NULL), codecOptions_(codecOptions)
54 | {
55 | }
56 |
57 | virtual ~Encoder()
58 | {
59 | }
60 |
61 | /// The listener will receive the encoded stream
62 | virtual void init(EncoderListener* listener, const SampleFormat& format)
63 | {
64 | if (codecOptions_ == "")
65 | codecOptions_ = getDefaultOptions();
66 | listener_ = listener;
67 | sampleFormat_ = format;
68 | initEncoder();
69 | }
70 |
71 | /// Here the work is done. Encoded data is passed to the EncoderListener.
72 | virtual void encode(const msg::PcmChunk* chunk) = 0;
73 |
74 | virtual std::string name() const = 0;
75 |
76 | virtual std::string getAvailableOptions() const
77 | {
78 | return "No codec options supported";
79 | }
80 |
81 | virtual std::string getDefaultOptions() const
82 | {
83 | return "";
84 | }
85 |
86 | /// Header information needed to decode the data
87 | virtual std::shared_ptr getHeader() const
88 | {
89 | return headerChunk_;
90 | }
91 |
92 | protected:
93 | virtual void initEncoder() = 0;
94 |
95 | SampleFormat sampleFormat_;
96 | std::shared_ptr headerChunk_;
97 | EncoderListener* listener_;
98 | std::string codecOptions_;
99 | };
100 |
101 |
102 | #endif
103 |
104 |
105 |
--------------------------------------------------------------------------------
/server/encoder/encoderFactory.cpp:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #include "encoderFactory.h"
20 | #include "pcmEncoder.h"
21 | #include "oggEncoder.h"
22 | #include "flacEncoder.h"
23 | #include "common/utils/string_utils.h"
24 | #include "common/snapException.h"
25 | #include "aixlog.hpp"
26 |
27 |
28 | using namespace std;
29 |
30 |
31 | Encoder* EncoderFactory::createEncoder(const std::string& codecSettings) const
32 | {
33 | Encoder* encoder;
34 | std::string codec(codecSettings);
35 | std::string codecOptions;
36 | if (codec.find(":") != std::string::npos)
37 | {
38 | codecOptions = utils::string::trim_copy(codec.substr(codec.find(":") + 1));
39 | codec = utils::string::trim_copy(codec.substr(0, codec.find(":")));
40 | }
41 | if (codec == "ogg")
42 | encoder = new OggEncoder(codecOptions);
43 | else if (codec == "pcm")
44 | encoder = new PcmEncoder(codecOptions);
45 | else if (codec == "flac")
46 | encoder = new FlacEncoder(codecOptions);
47 | else
48 | {
49 | throw SnapException("unknown codec: " + codec);
50 | }
51 |
52 | return encoder;
53 | /* try
54 | {
55 | encoder->init(NULL, format, codecOptions);
56 | }
57 | catch (const std::exception& e)
58 | {
59 | cout << "Error: " << e.what() << "\n";
60 | return 1;
61 | }
62 | */
63 | }
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/server/encoder/encoderFactory.h:
--------------------------------------------------------------------------------
1 | #ifndef ENCODER_FACTORY_H
2 | #define ENCODER_FACTORY_H
3 |
4 | #include
5 | #include "encoder.h"
6 |
7 | class EncoderFactory
8 | {
9 | public:
10 | // EncoderFactory(const std::string& codecSettings);
11 | Encoder* createEncoder(const std::string& codecSettings) const;
12 | };
13 |
14 |
15 | #endif
16 |
--------------------------------------------------------------------------------
/server/encoder/flacEncoder.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef FLAC_ENCODER_H
20 | #define FLAC_ENCODER_H
21 | #include "encoder.h"
22 | #include
23 | #include
24 | #include
25 |
26 | #include "FLAC/metadata.h"
27 | #include "FLAC/stream_encoder.h"
28 |
29 |
30 | class FlacEncoder : public Encoder
31 | {
32 | public:
33 | FlacEncoder(const std::string& codecOptions = "");
34 | ~FlacEncoder();
35 | virtual void encode(const msg::PcmChunk* chunk);
36 | virtual std::string getAvailableOptions() const;
37 | virtual std::string getDefaultOptions() const;
38 | virtual std::string name() const;
39 |
40 | FLAC__StreamEncoderWriteStatus write_callback(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame);
41 |
42 | protected:
43 | virtual void initEncoder();
44 |
45 | FLAC__StreamEncoder *encoder_;
46 | FLAC__StreamMetadata *metadata_[2];
47 |
48 | FLAC__int32 *pcmBuffer_;
49 | int pcmBufferSize_;
50 |
51 | msg::PcmChunk* flacChunk_;
52 | size_t encodedSamples_;
53 | };
54 |
55 |
56 | #endif
57 |
58 |
59 |
--------------------------------------------------------------------------------
/server/encoder/oggEncoder.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef OGG_ENCODER_H
20 | #define OGG_ENCODER_H
21 | #include "encoder.h"
22 | #include
23 |
24 |
25 | class OggEncoder : public Encoder
26 | {
27 | public:
28 | OggEncoder(const std::string& codecOptions = "");
29 | virtual void encode(const msg::PcmChunk* chunk);
30 | virtual std::string getAvailableOptions() const;
31 | virtual std::string getDefaultOptions() const;
32 | virtual std::string name() const;
33 |
34 | protected:
35 | virtual void initEncoder();
36 |
37 | private:
38 | ogg_stream_state os_; /// take physical pages, weld into a logical stream of packets
39 | ogg_page og_; /// one Ogg bitstream page. Vorbis packets are inside
40 | ogg_packet op_; /// one raw packet of data for decode
41 |
42 | vorbis_info vi_; /// struct that stores all the static vorbis bitstream settings
43 | vorbis_comment vc_; /// struct that stores all the user comments
44 |
45 | vorbis_dsp_state vd_; /// central working state for the packet->PCM decoder
46 | vorbis_block vb_; /// local working space for packet->PCM decode
47 |
48 | ogg_int64_t lastGranulepos_;
49 | };
50 |
51 |
52 | #endif
53 |
54 |
55 |
--------------------------------------------------------------------------------
/server/encoder/pcmEncoder.cpp:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #include
20 | #include "common/endian.h"
21 | #include "pcmEncoder.h"
22 |
23 |
24 | #define ID_RIFF 0x46464952
25 | #define ID_WAVE 0x45564157
26 | #define ID_FMT 0x20746d66
27 | #define ID_DATA 0x61746164
28 |
29 |
30 | PcmEncoder::PcmEncoder(const std::string& codecOptions) : Encoder(codecOptions)
31 | {
32 | headerChunk_.reset(new msg::CodecHeader("pcm"));
33 | }
34 |
35 |
36 | void PcmEncoder::encode(const msg::PcmChunk* chunk)
37 | {
38 | msg::PcmChunk* pcmChunk = new msg::PcmChunk(*chunk);
39 | listener_->onChunkEncoded(this, pcmChunk, pcmChunk->duration().count());
40 | }
41 |
42 |
43 | void PcmEncoder::initEncoder()
44 | {
45 | headerChunk_->payloadSize = 44;
46 | headerChunk_->payload = (char*)malloc(headerChunk_->payloadSize);
47 | char* payload = headerChunk_->payload;
48 | assign(payload, SWAP_32(ID_RIFF));
49 | assign(payload + 4, SWAP_32(36));
50 | assign(payload + 8, SWAP_32(ID_WAVE));
51 | assign(payload + 12, SWAP_32(ID_FMT));
52 | assign(payload + 16, SWAP_32(16));
53 | assign(payload + 20, SWAP_16(1));
54 | assign(payload + 22, SWAP_16(sampleFormat_.channels));
55 | assign(payload + 24, SWAP_32(sampleFormat_.rate));
56 | assign(payload + 28, SWAP_32(sampleFormat_.rate * sampleFormat_.bits * sampleFormat_.channels / 8));
57 | assign(payload + 32, SWAP_16(sampleFormat_.channels * ((sampleFormat_.bits + 7) / 8)));
58 | assign(payload + 34, SWAP_16(sampleFormat_.bits));
59 | assign(payload + 36, SWAP_32(ID_DATA));
60 | assign(payload + 40, SWAP_32(0));
61 | }
62 |
63 |
64 | std::string PcmEncoder::name() const
65 | {
66 | return "pcm";
67 | }
68 |
69 |
70 |
--------------------------------------------------------------------------------
/server/encoder/pcmEncoder.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef PCM_ENCODER_H
20 | #define PCM_ENCODER_H
21 | #include "encoder.h"
22 |
23 |
24 | class PcmEncoder : public Encoder
25 | {
26 | public:
27 | PcmEncoder(const std::string& codecOptions = "");
28 | virtual void encode(const msg::PcmChunk* chunk);
29 | virtual std::string name() const;
30 |
31 | protected:
32 | virtual void initEncoder();
33 |
34 | template
35 | void assign(void* pointer, T val)
36 | {
37 | T* p = (T*)pointer;
38 | *p = val;
39 | }
40 | };
41 |
42 |
43 | #endif
44 |
45 |
46 |
--------------------------------------------------------------------------------
/server/publishZeroConf/publishAvahi.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 |
20 | #ifndef PUBLISH_AVAHI_H
21 | #define PUBLISH_AVAHI_H
22 |
23 | #include
24 | #include
25 |
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 |
36 | class PublishAvahi;
37 |
38 | #include "publishmDNS.h"
39 |
40 | class PublishAvahi : public PublishmDNS
41 | {
42 | public:
43 | PublishAvahi(const std::string& serviceName);
44 | virtual ~PublishAvahi();
45 | virtual void publish(const std::vector& services);
46 |
47 | private:
48 | static void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata);
49 | static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata);
50 | void create_services(AvahiClient *c);
51 | void worker();
52 | AvahiClient* client_;
53 | std::thread pollThread_;
54 | std::atomic active_;
55 | std::vector services_;
56 | };
57 |
58 |
59 | #endif
60 |
61 |
62 |
--------------------------------------------------------------------------------
/server/publishZeroConf/publishBonjour.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 |
20 | #ifndef PUBLISH_BONJOUR_H
21 | #define PUBLISH_BONJOUR_H
22 |
23 | #include
24 | #include
25 |
26 | class PublishBonjour;
27 |
28 | #include "publishmDNS.h"
29 |
30 | class PublishBonjour : public PublishmDNS
31 | {
32 | public:
33 | PublishBonjour(const std::string& serviceName);
34 | virtual ~PublishBonjour();
35 | virtual void publish(const std::vector& services);
36 |
37 | private:
38 | std::thread pollThread_;
39 | void worker();
40 | std::atomic active_;
41 | std::vector clients;
42 | };
43 |
44 |
45 | #endif
46 |
47 |
48 |
--------------------------------------------------------------------------------
/server/publishZeroConf/publishmDNS.h:
--------------------------------------------------------------------------------
1 | #ifndef PUBLISH_MDNS_H
2 | #define PUBLISH_MDNS_H
3 |
4 | #include
5 | #include
6 |
7 |
8 | struct mDNSService
9 | {
10 | mDNSService(const std::string& name, size_t port) : name_(name), port_(port)
11 | {
12 | }
13 |
14 | std::string name_;
15 | size_t port_;
16 | };
17 |
18 |
19 | class PublishmDNS
20 | {
21 | public:
22 | PublishmDNS(const std::string& serviceName) : serviceName_(serviceName)
23 | {
24 | }
25 |
26 | virtual ~PublishmDNS()
27 | {
28 | }
29 |
30 | virtual void publish(const std::vector& services) = 0;
31 |
32 | protected:
33 | std::string serviceName_;
34 | };
35 |
36 | #if defined(HAS_AVAHI)
37 | #include "publishAvahi.h"
38 | typedef PublishAvahi PublishZeroConf;
39 | #elif defined(HAS_BONJOUR)
40 | #include "publishBonjour.h"
41 | typedef PublishBonjour PublishZeroConf;
42 | #endif
43 |
44 | #endif
45 |
--------------------------------------------------------------------------------
/server/snapserver.1:
--------------------------------------------------------------------------------
1 | .TH SNAPSERVER 1 "December 2015"
2 | .SH NAME
3 | snapserver - Snapcast server
4 | .SH SYNOPSIS
5 | \fBsnapserver\fR [\fIOPTION\fR]...
6 | .SH DESCRIPTION
7 | Snapcast is a multi-room client-server audio player, where all clients are
8 | time synchronized with the server to play perfectly synced audio. It's not a
9 | standalone player, but an extension that turns your existing audio player into
10 | a Sonos-like multi-room solution.
11 | .br
12 | The server's audio input is a named pipe `/tmp/snapfifo`. All data that is fed
13 | into this file will be send to the connected clients. One of the most generic
14 | ways to use Snapcast is in conjunction with the music player daemon or Mopidy,
15 | which can be configured to use a named pipe as audio output.
16 | .SS Options
17 | .TP
18 | \fB-h, --help\fR
19 | produce help message
20 | .TP
21 | \fB-v, --version\fR
22 | show version number
23 | .TP
24 | \fB-p, --port\fR
25 | server port
26 | .TP
27 | \fB--controlPort\fR
28 | Remote control port (default =1705)
29 | .TP
30 | \fB-s, --stream\fR
31 | URI of the PCM input stream. Format:
32 | .br
33 | TYPE://host/path?name=NAME[&codec=CODEC][&sampleformat=SAMPLEFORMAT]
34 | .TP
35 | \fB--sampleformat\fR
36 | default sample format (default = 48000:16:2)
37 | .TP
38 | \fB--codec\fR
39 | default transport codec [flac|ogg|pcm][:options]. Type codec:? to get codec specific options
40 | .TP
41 | \fB--streamBuffer\fR
42 | Default stream read buffer [ms] (default = 20)
43 | .TP
44 | \fB-b, --buffer\fR
45 | buffer [ms] (default = 1000)
46 | .TP
47 | \fB-b, --sendToMuted\fR
48 | Send audio to muted clients
49 | .TP
50 | \fB-d, --daemon\fR
51 | daemonize, optional process priority [-20..19]
52 | .TP
53 | \fB--user\fR
54 | the user[:group] to run snapserver as when daemonized
55 | .SH FILES
56 | .TP
57 | \fI/tmp/snapfifo\fR
58 | PCM input fifo file
59 | .TP
60 | \fI/etc/default/snapserver\fR
61 | the daemon default configuration file
62 | .TP
63 | \fI~/.config/snapcast/server.json\fR or (if $HOME is not set) \fI/var/lib/snapcast/server.json\fR
64 | persistent server data file
65 | .SH "COPYRIGHT"
66 | Copyright (C) 2014-2017 Johannes Pohl (johannes.pohl@badaix.de).
67 | License GPLv3+: GNU GPL version 3 or later .
68 | This is free software: you are free to change and redistribute it.
69 | There is NO WARRANTY, to the extent permitted by law.
70 | .SH "SEE ALSO"
71 | snapclient(1)
72 |
--------------------------------------------------------------------------------
/server/streamreader/airplayStream.cpp:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #include "airplayStream.h"
20 | #include "common/snapException.h"
21 | #include "common/utils/string_utils.h"
22 | #include "common/utils.h"
23 | #include "aixlog.hpp"
24 |
25 |
26 | using namespace std;
27 |
28 |
29 |
30 |
31 | AirplayStream::AirplayStream(PcmListener* pcmListener, const StreamUri& uri) : ProcessStream(pcmListener, uri), port_(5000)
32 | {
33 | logStderr_ = true;
34 |
35 | sampleFormat_ = SampleFormat("44100:16:2");
36 | uri_.query["sampleformat"] = sampleFormat_.getFormat();
37 |
38 | port_ = cpt::stoul(uri_.getQuery("port", "5000"));
39 |
40 | string devicename = uri_.getQuery("devicename", "Snapcast");
41 | params_wo_port_ = "--name=\"" + devicename + "\" --output=stdout";
42 | params_ = params_wo_port_ + " --port=" + cpt::to_string(port_);
43 | }
44 |
45 |
46 | AirplayStream::~AirplayStream()
47 | {
48 | }
49 |
50 |
51 | void AirplayStream::initExeAndPath(const std::string& filename)
52 | {
53 | path_ = "";
54 | exe_ = findExe(filename);
55 | if (!fileExists(exe_) || (exe_ == "/"))
56 | {
57 | exe_ = findExe("shairport-sync");
58 | if (!fileExists(exe_))
59 | throw SnapException("shairport-sync not found");
60 | }
61 |
62 | if (exe_.find("/") != string::npos)
63 | {
64 | path_ = exe_.substr(0, exe_.find_last_of("/") + 1);
65 | exe_ = exe_.substr(exe_.find_last_of("/") + 1);
66 | }
67 | }
68 |
69 |
70 | void AirplayStream::onStderrMsg(const char* buffer, size_t n)
71 | {
72 | string logmsg = utils::string::trim_copy(string(buffer, n));
73 | if (logmsg.empty())
74 | return;
75 | LOG(INFO) << "(" << getName() << ") " << logmsg << "\n";
76 | if (logmsg.find("Is another Shairport Sync running on this device") != string::npos)
77 | {
78 | LOG(ERROR) << "Seem there is another Shairport Sync runnig on port " << port_ << ", switching to port " << port_ + 1 << "\n";
79 | ++port_;
80 | params_ = params_wo_port_ + " --port=" + cpt::to_string(port_);
81 | }
82 | else if (logmsg.find("Invalid audio output specified") != string::npos)
83 | {
84 | LOG(ERROR) << "shairport sync compiled without stdout audio backend\n";
85 | LOG(ERROR) << "build with: \"./configure --with-stdout --with-avahi --with-ssl=openssl --with-metadata\"\n";
86 | }
87 | }
88 |
89 |
--------------------------------------------------------------------------------
/server/streamreader/airplayStream.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef AIRPLAY_STREAM_H
20 | #define AIRPLAY_STREAM_H
21 |
22 | #include "processStream.h"
23 |
24 | /// Starts shairport-sync and reads PCM data from stdout
25 | /**
26 | * Starts librespot, reads PCM data from stdout, and passes the data to an encoder.
27 | * Implements EncoderListener to get the encoded data.
28 | * Data is passed to the PcmListener
29 | * usage:
30 | * snapserver -s "airplay:///shairport-sync?name=Airplay[&devicename=Snapcast][&port=5000]"
31 | */
32 | class AirplayStream : public ProcessStream
33 | {
34 | public:
35 | /// ctor. Encoded PCM data is passed to the PipeListener
36 | AirplayStream(PcmListener* pcmListener, const StreamUri& uri);
37 | virtual ~AirplayStream();
38 |
39 | protected:
40 | virtual void onStderrMsg(const char* buffer, size_t n);
41 | virtual void initExeAndPath(const std::string& filename);
42 | size_t port_;
43 | std::string params_wo_port_;
44 | };
45 |
46 |
47 | #endif
48 |
--------------------------------------------------------------------------------
/server/streamreader/fileStream.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef FILE_STREAM_H
20 | #define FILE_STREAM_H
21 |
22 | #include "pcmStream.h"
23 | #include
24 |
25 |
26 | /// Reads and decodes PCM data from a file
27 | /**
28 | * Reads PCM from a file and passes the data to an encoder.
29 | * Implements EncoderListener to get the encoded data.
30 | * Data is passed to the PcmListener
31 | */
32 | class FileStream : public PcmStream
33 | {
34 | public:
35 | /// ctor. Encoded PCM data is passed to the PipeListener
36 | FileStream(PcmListener* pcmListener, const StreamUri& uri);
37 | virtual ~FileStream();
38 |
39 | protected:
40 | virtual void worker();
41 | std::ifstream ifs;
42 | };
43 |
44 |
45 | #endif
46 |
--------------------------------------------------------------------------------
/server/streamreader/pipeStream.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef PIPE_STREAM_H
20 | #define PIPE_STREAM_H
21 |
22 | #include "pcmStream.h"
23 |
24 |
25 |
26 | /// Reads and decodes PCM data from a named pipe
27 | /**
28 | * Reads PCM from a named pipe and passes the data to an encoder.
29 | * Implements EncoderListener to get the encoded data.
30 | * Data is passed to the PcmListener
31 | */
32 | class PipeStream : public PcmStream
33 | {
34 | public:
35 | /// ctor. Encoded PCM data is passed to the PipeListener
36 | PipeStream(PcmListener* pcmListener, const StreamUri& uri);
37 | virtual ~PipeStream();
38 |
39 | protected:
40 | virtual void worker();
41 | int fd_;
42 | };
43 |
44 |
45 | #endif
46 |
--------------------------------------------------------------------------------
/server/streamreader/processStream.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef PROCESS_STREAM_H
20 | #define PROCESS_STREAM_H
21 |
22 | #include
23 | #include
24 |
25 | #include "pcmStream.h"
26 | #include "process.hpp"
27 |
28 |
29 | /// Starts an external process and reads and PCM data from stdout
30 | /**
31 | * Starts an external process, reads PCM data from stdout, and passes the data to an encoder.
32 | * Implements EncoderListener to get the encoded data.
33 | * Data is passed to the PcmListener
34 | */
35 | class ProcessStream : public PcmStream
36 | {
37 | public:
38 | /// ctor. Encoded PCM data is passed to the PipeListener
39 | ProcessStream(PcmListener* pcmListener, const StreamUri& uri);
40 | virtual ~ProcessStream();
41 |
42 | virtual void start();
43 | virtual void stop();
44 |
45 | protected:
46 | std::string exe_;
47 | std::string path_;
48 | std::string params_;
49 | std::unique_ptr process_;
50 | std::thread stderrReaderThread_;
51 | bool logStderr_;
52 |
53 | virtual void worker();
54 | virtual void stderrReader();
55 | virtual void onStderrMsg(const char* buffer, size_t n);
56 | virtual void initExeAndPath(const std::string& filename);
57 |
58 | bool fileExists(const std::string& filename);
59 | std::string findExe(const std::string& filename);
60 | };
61 |
62 |
63 | #endif
64 |
--------------------------------------------------------------------------------
/server/streamreader/spotifyStream.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef SPOTIFY_STREAM_H
20 | #define SPOTIFY_STREAM_H
21 |
22 | #include "processStream.h"
23 | #include "watchdog.h"
24 |
25 | /// Starts librespot and reads PCM data from stdout
26 | /**
27 | * Starts librespot, reads PCM data from stdout, and passes the data to an encoder.
28 | * Implements EncoderListener to get the encoded data.
29 | * Data is passed to the PcmListener
30 | * usage:
31 | * snapserver -s "spotify:///librespot?name=Spotify&username=&password=[&devicename=Snapcast][&bitrate=320][&cache=]"
32 | */
33 | class SpotifyStream : public ProcessStream, WatchdogListener
34 | {
35 | public:
36 | /// ctor. Encoded PCM data is passed to the PipeListener
37 | SpotifyStream(PcmListener* pcmListener, const StreamUri& uri);
38 | virtual ~SpotifyStream();
39 |
40 | protected:
41 | std::unique_ptr watchdog_;
42 |
43 | virtual void stderrReader();
44 | virtual void onStderrMsg(const char* buffer, size_t n);
45 | virtual void initExeAndPath(const std::string& filename);
46 |
47 | virtual void onTimeout(const Watchdog* watchdog, size_t ms);
48 | };
49 |
50 |
51 | #endif
52 |
--------------------------------------------------------------------------------
/server/streamreader/streamManager.h:
--------------------------------------------------------------------------------
1 | #ifndef PCM_READER_FACTORY_H
2 | #define PCM_READER_FACTORY_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include "pcmStream.h"
8 |
9 | typedef std::shared_ptr PcmStreamPtr;
10 |
11 | class StreamManager
12 | {
13 | public:
14 | StreamManager(PcmListener* pcmListener, const std::string& defaultSampleFormat, const std::string& defaultCodec, size_t defaultReadBufferMs = 20);
15 |
16 | PcmStreamPtr addStream(const std::string& uri);
17 | void start();
18 | void stop();
19 | const std::vector& getStreams();
20 | const PcmStreamPtr getDefaultStream();
21 | const PcmStreamPtr getStream(const std::string& id);
22 | json toJson() const;
23 |
24 | private:
25 | std::vector streams_;
26 | PcmListener* pcmListener_;
27 | std::string sampleFormat_;
28 | std::string codec_;
29 | size_t readBufferMs_;
30 | };
31 |
32 |
33 | #endif
34 |
--------------------------------------------------------------------------------
/server/streamreader/streamUri.h:
--------------------------------------------------------------------------------
1 | /***
2 | This file is part of snapcast
3 | Copyright (C) 2014-2017 Johannes Pohl
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | ***/
18 |
19 | #ifndef READER_URI_H
20 | #define READER_URI_H
21 |
22 | #include
23 | #include