• WANTED: Happy members who like to discuss audio and other topics related to our interest. Desire to learn and share knowledge of science required. There are many reviews of audio hardware and expert members to help answer your questions. Click here to have your audio equipment measured for free!

WiiM Mini Streamer

thank you.....
will it be possible as an option to see the "true" flows in real time?
 
I think the Wiim developers have been responsible and take reasonable times to solve the issues. I don't mind to wait a couple of weeks more if that grants the issue to be solved.
For comparison, if you use the idagio app, they only implemented the gapless playing for Android 1 year after they delivered to iOS, a basic feature in a classical music app. And idagio has hundreds of thousands of downloads in the Play Store.
 
One thing missing on the WiiM Mini that's still there on the older A31-based devices: the ability to monitor the currently playing track via http. Http is completely disabled on the Wiim Mini, likely for security reasons.

It was pretty basic, but here's what it looked like in the Chrome browser on the older devices.

1650573181811.png
 
Some good news for those of us who like to muck about with coding. The UPnP interface on the WiiM Mini will report full metadata whether or not you're streaming to it via UPnP. Here's the info pulled by invoking a query in UPnP Tool from my Chromebox, complete with album art URL. Building a display for this will be straightforward... :p

XML:
<?xml version="1.0" encoding="UTF-8"?> <DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:song="www.wiimu.com/song/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"> <upnp:class>object.item.audioItem.musicTrack</upnp:class> <item id="0"> <song:subid></song:subid> <song:description></song:description> <song:skiplimit>0</song:skiplimit> <song:id>219657692</song:id> <song:like>0</song:like> <song:singerid>1070</song:singerid> <song:albumid>219657691</song:albumid> <song:quality>1</song:quality> <res protocolInfo="http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;" duration="00:18:34.000">wiimu_search://219657692</res> <dc:title>The Man Who Never Sleeps</dc:title> <dc:creator>Charles Mingus</dc:creator> <upnp:artist>Charles Mingus</upnp:artist> <upnp:album>The Man Who Never Sleeps (Live)</upnp:album> <upnp:albumArtURI>http://resources.wimpmusic.com/images/e554b38f/2125/4cf7/a863/377962bc1da9/640x640.jpg</upnp:albumArtURI> </item> </DIDL-Lite>

640x640.jpg
 
I have the Mini and I am happy with it. Looking at ASR's list of streamers and it seems to place a head or equal to the Bluesound. Its' performance is bested by the Pi based solutions (DACs are better) and ranks 3rd in SINAD. Once connected to an external DAC (Soncoz LXD1) how could I complain? When using Alexa and the Mini as an Alexa slave, do we know the optical output in Bitdepth and rate using Amason HD Music files for the Mini?
Any insight would be appreciated. Multi-room is a wonderful world and so easy now.
 
I have the Mini and I am happy with it. Looking at ASR's list of streamers and it seems to place a head or equal to the Bluesound. Its' performance is bested by the Pi based solutions (DACs are better) and ranks 3rd in SINAD. Once connected to an external DAC (Soncoz LXD1) how could I complain? When using Alexa and the Mini as an Alexa slave, do we know the optical output in Bitdepth and rate using Amason HD Music files for the Mini?
Any insight would be appreciated. Multi-room is a wonderful world and so easy now.
It doesn’t yet support the lossless Amazon Music HD service, just the default lossy Amazon Unlimited (as was) ‘SD’ tracks.
 
Thks, maybe it will get better or is it an Alexa thing?
As mentioned several times in the thread, and on their website, WiiM hope to have support in the WiiM app for lossless 16 bit HD and 24 bit UHD tracks in a month’s time. Whether playback via Alexa or casting from the Amazon Music app will be lossless all the way up to 24/192 remains to be seen tbh - they have claimed it will be but if they do achieve UHD casting, they could well be the first to do so.
 
I have both a Bluesound and a WiiM and Bluesound still has quite a few advantages - an Ethernet Port, Digital Coax out, USB drive support. I think the DAC in the Bluesound sounds much better and has full MQA decoding with Tidal. Software wise, Bluesound supports Amazon hi res, is Roon ready and the app is slightly better. Bluesound has no uPNP though, but WiiM has dropouts with Audirvāna over uPNP. I’m hopeful the WiiM chaps will add more features over time
 
Some good news for those of us who like to muck about with coding. The UPnP interface on the WiiM Mini will report full metadata whether or not you're streaming to it via UPnP. Here's the info pulled by invoking a query in UPnP Tool from my Chromebox, complete with album art URL. Building a display for this will be straightforward... :p

XML:
<?xml version="1.0" encoding="UTF-8"?> <DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:song="www.wiimu.com/song/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"> <upnp:class>object.item.audioItem.musicTrack</upnp:class> <item id="0"> <song:subid></song:subid> <song:description></song:description> <song:skiplimit>0</song:skiplimit> <song:id>219657692</song:id> <song:like>0</song:like> <song:singerid>1070</song:singerid> <song:albumid>219657691</song:albumid> <song:quality>1</song:quality> <res protocolInfo="http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;" duration="00:18:34.000">wiimu_search://219657692</res> <dc:title>The Man Who Never Sleeps</dc:title> <dc:creator>Charles Mingus</dc:creator> <upnp:artist>Charles Mingus</upnp:artist> <upnp:album>The Man Who Never Sleeps (Live)</upnp:album> <upnp:albumArtURI>http://resources.wimpmusic.com/images/e554b38f/2125/4cf7/a863/377962bc1da9/640x640.jpg</upnp:albumArtURI> </item> </DIDL-Lite>
As it turns out, the WiiM appears to be putting the <upnp:class/> in the wrong place when playing from its app, which the Python DIDL-Lite parser refuses to parse. It's fine (inside <item/>) when being controlled by external UPnP servers.

So, I simply convert the godawful XML to lovely JSON, and everything's great.

Here's a very basic Python script that subscribes to the AVTransport service of the WiiM, and continuously monitors and prints the metadata. Needs to be hooked to a display, and the album art pulled and displayed, but the underlying code is working nicely. Just repoint it to your WiiM's URI.


Python:
# -*- coding: utf-8 -*-
# pylint: disable=invalid-name

import argparse
import asyncio
import json
import logging
import operator
import sys
import time
import xmltodict
from datetime import datetime
from typing import Any, Optional, Sequence, Tuple, Union, cast

from didl_lite import didl_lite
NAMESPACES = {
    "didl_lite": "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/",
    "dc": "http://purl.org/dc/elements/1.1/",
    "upnp": "urn:schemas-upnp-org:metadata-1-0/upnp/",
    "xsi": "http://www.w3.org/2001/XMLSchema-instance",
    "song": "www.wiimu.com/song/",
    "custom": "www.linkplay.com/custom/",
}


from async_upnp_client.advertisement import SsdpAdvertisementListener
from async_upnp_client.aiohttp import AiohttpNotifyServer, AiohttpRequester
from async_upnp_client.client import UpnpDevice, UpnpService, UpnpStateVariable
from async_upnp_client.client_factory import UpnpFactory
from async_upnp_client.const import NS, AddressTupleVXType, SsdpHeaders
from async_upnp_client.exceptions import UpnpResponseError
from async_upnp_client.profiles.dlna import dlna_handle_notify_last_change
from async_upnp_client.search import async_search as async_ssdp_search
from async_upnp_client.ssdp import SSDP_IP_V4, SSDP_IP_V6, SSDP_PORT, SSDP_ST_ALL
from async_upnp_client.utils import get_local_ip

logging.basicConfig()
_LOGGER = logging.getLogger("upnp-client")
_LOGGER.setLevel(logging.ERROR)
_LOGGER_LIB = logging.getLogger("async_upnp_client")
_LOGGER_LIB.setLevel(logging.ERROR)
_LOGGER_TRAFFIC = logging.getLogger("async_upnp_client.traffic")
_LOGGER_TRAFFIC.setLevel(logging.ERROR)

pprint_indent = 4

event_handler = None

async def create_device(description_url: str) -> UpnpDevice:
    """Create UpnpDevice."""
    timeout = 60
    non_strict = True
    requester = AiohttpRequester(timeout)
    factory = UpnpFactory(requester, non_strict=non_strict)
    return await factory.async_create_device(description_url)


def get_timestamp() -> Union[str, float]:
    """Timestamp depending on configuration."""
    return time.time()


def service_from_device(device: UpnpDevice, service_name: str) -> Optional[UpnpService]:
    """Get UpnpService from UpnpDevice by name or part or abbreviation."""
    for service in device.all_services:
        part = service.service_id.split(":")[-1]
        abbr = "".join([c for c in part if c.isupper()])
        if service_name in (service.service_type, part, abbr):
            return service

    return None


def on_event(
    service: UpnpService, service_variables: Sequence[UpnpStateVariable]
) -> None:
    """Handle a UPnP event."""
    _LOGGER.debug(
        "State variable change for %s, variables: %s",
        service,
        ",".join([sv.name for sv in service_variables]),
    )
    obj = {
        "timestamp": get_timestamp(),
        "service_id": service.service_id,
        "service_type": service.service_type,
        "state_variables": {sv.name: sv.value for sv in service_variables},
    }

    # special handling for DLNA LastChange state variable
    if len(service_variables) == 1 and service_variables[0].name == "LastChange":
        last_change = service_variables[0]
        dlna_handle_notify_last_change(last_change)
    else:
        for sv in service_variables:
            ### PAUSED, PLAYING, STOPPED, etc
            if sv.name == "TransportState":
                print(sv.value)

            ### Grab and print the metadata
            if sv.name == "AVTransportURIMetaData":
                ### Convert the grubby XML to beautiful JSON, because we HATE XML!
                items = xmltodict.parse(sv.value)["DIDL-Lite"]["item"]
                ### Print the entire mess
                print(json.dumps(items,indent=4))

                ### Print each item of interest
                try:
                  title = items["dc:title"]
                  print("Title:",title)
                except:
                  pass

                try:
                  subtitle = items["dc:subtitle"]
                  print("Subtitle:",subtitle)
                except:
                  pass

                try:
                  artist = items["upnp:artist"]
                  print("Artist:",artist)
                except:
                  pass

                try:
                  album = items["upnp:album"]
                  print("Album:",album)
                except:
                  pass
             
                try:
                  art = items["upnp:albumArtURI"]
                  print("Art:",art)
                except:
                  pass
               

async def subscribe(description_url: str, service_names: Any) -> None:
    """Subscribe to service(s) and output updates."""
    global event_handler  # pylint: disable=global-statement

    device = await create_device(description_url)

    # start notify server/event handler
    source = (get_local_ip(device.device_url), 0)
    server = AiohttpNotifyServer(device.requester, source=source)
    await server.async_start_server()
    _LOGGER.debug("Listening on: %s", server.callback_url)

    # gather all wanted services
    if "*" in service_names:
        service_names = device.services.keys()

    services = []
   
    for service_name in service_names:
        service = service_from_device(device, service_name)
        if not service:
            print(f"Unknown service: {service_name}")
            sys.exit(1)
        service.on_event = on_event
        services.append(service)

    # subscribe to services
    event_handler = server.event_handler
    for service in services:
        try:
            await event_handler.async_subscribe(service)
        except UpnpResponseError as ex:
            _LOGGER.error("Unable to subscribe to %s: %s", service, ex)

    # keep the webservice running
    while True:
        await asyncio.sleep(120)
        await event_handler.async_resubscribe_all()

async def async_main() -> None:
    """Async main."""

    ####  NOTICE!!!! #####################################
    ####  Your WiiM Mini's IP and port go here
    device = "http://192.168.68.112:49152/description.xml"
    ####             #####################################
    service = ["AVTransport"]

    await subscribe(device, service)

def main() -> None:
    """Set up async loop and run the main program."""
    loop = asyncio.get_event_loop()

    try:
        loop.run_until_complete(async_main())
    except KeyboardInterrupt:
        if event_handler:
            loop.run_until_complete(event_handler.async_unsubscribe_all())
    finally:
        loop.close()


if __name__ == "__main__":
    main()

Code:
STOPPED
PAUSED_PLAYBACK
{
    "@id": "0",
    "song:subid": null,
    "song:description": null,
    "song:skiplimit": "0",
    "song:id": null,
    "song:like": "0",
    "song:singerid": "0",
    "song:albumid": "0",
    "song:quality": "0",
    "res": {
        "@protocolInfo": "http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;",
        "@duration": "00:05:32.000"
    },
    "dc:title": "That Tired Routine Called Love",
    "dc:creator": "Jan Lundgren, Tom Warrington, Joe La Barbera, Ted Steele",
    "upnp:artist": "Jan Lundgren, Tom Warrington, Joe La Barbera, Ted Steele",
    "upnp:album": "Celebrating the Music of Matt Dennis: Will You Still Be Mine?",
    "upnp:albumArtURI": "https://resources.tidal.com/images/b57d5c14/592d/44ac/be3b/40c0fae82519/640x640.jpg"
}
Title: That Tired Routine Called Love
Artist: Jan Lundgren, Tom Warrington, Joe La Barbera, Ted Steele
Album: Celebrating the Music of Matt Dennis: Will You Still Be Mine?
Art: https://resources.tidal.com/images/b57d5c14/592d/44ac/be3b/40c0fae82519/640x640.jpg
PAUSED_PLAYBACK
STOPPED
{
    "@id": "216411017",
    "@parentID": "0",
    "@restricted": "0",
    "upnp:class": "object.item.audioItem.musicTrack",
    "dc:title": "maybe as his skies are wide",
    "upnp:artist": "Brad Mehldau",
    "upnp:album": "Jacob's Ladder",
    "upnp:albumArtURI": {
        "@dlna:profileID": "JPEG_TN",
        "@xmlns:dlna": "urn:schemas-dlna-org:metadata-1-0/",
        "#text": "http://192.168.68.130:14406/tidal/images/216411017.jpg"
    },
    "res": {
        "@duration": "00:03:43",
        "@protocolInfo": "http-get:*:audio/x-flac:DLNA.ORG_PN=FLAC;DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=03700000000000000000000000000000",
        "#text": "http://192.168.68.130:14406/tidal/216411017.flac"
    }
}
Title: maybe as his skies are wide
Artist: Brad Mehldau
Album: Jacob's Ladder
Art: OrderedDict([('@dlna:profileID', 'JPEG_TN'), ('@xmlns:dlna', 'urn:schemas-dlna-org:metadata-1-0/'), ('#text', 'http://192.168.68.130:14406/tidal/images/216411017.jpg')])
STOPPED
TRANSITIONING
PLAYING
STOPPED
 
Thank you.

I spent a lot of time reading the various posts here and elsewhere on the Internet, but the conversation seems very confusing with lots of differing views, not to mention expensive solutions that are disproportionate to the cost of a WiiM Mini.

In case it helps anyone else, I chose to try an inexpensive USB Noise Isolator that solved my problem.
As a bit of follow up on this. Whilst the USB isolator helped, I noticed it didn’t completely remove the hum when I listened closely. I’ve since tried a Monacor FGA-30M which sits between the WiiM Aux Out and amplifier input. This seems to be even more effective.
 
In your earlier posts, you wrote that you were using the TOSLINK output port of the WiiM. Presumably that had no hum. Any reason you are trying to use the internal WiiM DAC via the Aux out? It's not very good according to Amir.
 
In your earlier posts, you wrote that you were using the TOSLINK output port of the WiiM. Presumably that had no hum. Any reason you are trying to use the internal WiiM DAC via the Aux out? It's not very good according to Amir.
I’ve used both outputs, but the reason I bought the WiiM was to incorporate an old analogue mini-HiFi system into my multi-room AirPlay setup. Adding a DAC with TOSLINK to that setup would have been an alternative solution, but disproportionate in terms of cost to achieve what I needed.

If I didn’t need the old HiFi system in that room for reasons other than an AirPlay destination, I would have replaced it with a pair of HomePod Minis.
 
I see. Did you consider the cheap generic DACs? Most of them have decent sound quality for what you are trying to do and won’t have hum. Here’s a $12 one

 
Back
Top Bottom