[Solved]AudioDeviceSelectorComponent not get along with ASIO

Dear Jules,

I think that AudioDdeviceSelectorComponent has a flaw concerning ASIO management.

When I change to ASIO or from ASIO to other device, sometimes an interrupt is triggered. here is the moment:

Any idea?

Gabriel

You image didn’t work, was it a stack-trace?

I’m just playing with ASIO myself, so would appreciate any info on how I could reproduce this?

Yes, it was a stack trace and where the code was halted.

Could you recommend me a picture repository?

Did you try adding the image to the post itself by uploading it as an attachment?

Here it is! (Thx jrlangois!)

[attachment=0]Sin título (1).png[/attachment]

welcome!

(Just an fyi, I don’t see a stack trace in there… in visual studio it’s referred to as “Call Stack” [not sure what that would be in español!])

pila de llamadas? 8)

That’s right!

Yes I didn’t notice to takesuch snapshot with the ‘pila de llamadas’ (Stack trace) on screen.

But, nothing interesting, there were 3 or 4 stacks calls about the dtors of a Juce String until a non well formed pointer (null or dangled…!) triggered such interrupt.

Nevertheless, tomorrow, I’ll try to show you such stacktrace

Jules,
Is it possible that Strings members from juce_AudioDeviceSelectorComponent class does not handle non ASCII glyphs, I mean UNICODE glyphs. Maybe it could be the origins of the bad behaviour of this Component.

It’s possible that in reading a string from an ASIO device to juce, the charset may be mis-matched. Ideally drivers would use UTF-8 to return their strings, but since Steinberg’s SDKs were never clear about charsets, the drivers could be returning any kind of format. You could try digging into the ASIO code and see what happens in there.

Agree, Jules. I’m digging into it. I think that the problem is not the ASIO device. It is, presumably, the Implementation of DirectSound device because it is in Spanish. So there are ñ, á, é,… etc…

I am delimiting the problem and the problem is idetected in juce_AudioDeviceSelectorComponent.cpp :

[code] class ChannelSelectorListBox : public ListBox,
private ListBoxModel
{
public:
enum BoxType
{
audioInputType,
audioOutputType
};

    //==============================================================================
    ChannelSelectorListBox (const AudioDeviceSetupDetails& setup_,
                            const BoxType type_,
                            const String& noItemsMessage_)
        : ListBox (String::empty, nullptr),
          setup (setup_),
          type (type_),
          noItemsMessage (noItemsMessage_)
    {
        refresh();
        setModel (this);
        setOutlineThickness (1);
    }

    void refresh()
    {
        items.clear();

        if (AudioIODevice* const currentDevice = setup.manager->getCurrentAudioDevice())
        {
            if (type == audioInputType)
                items = currentDevice->getInputChannelNames();
            else if (type == audioOutputType)
                items = currentDevice->getOutputChannelNames();

            if (setup.useStereoPairs)
            {
                StringArray pairs;

                for (int i = 0; i < items.size(); i += 2)
                {
                    const String& name = items[i];

                    if (i + 1 >= items.size())
                        pairs.add (name.trim());
                    else
                        pairs.add (getNameForChannelPair (name, items[i + 1]));
                }

                items = pairs;
            }
        }

        updateContent();
        repaint();
    }

    int getNumRows()
    {
        return items.size();
    }

[/code]

The StringArray items isn’t receiving well such UNICODE or UTF8 strings.

Despite I’m diving into it, any idea meanwhile?

Gabriel

No… You’re way off track, there’ll be no issue with anything outside of the raw ASIO (or DirectSound) driver code, because it’s all just juce strings, which are completely unicode-safe. The only place that could be a problem would be at the point where raw char* data is passed from the driver, and first becomes a String.

Jules,

I’ve delimited the issue. The problem is in:

           [code] g.drawText (item, x, 0, width - x - 2, height, Justification::centredLeft, true);[/code]

from method:

[code] void paintListBoxItem (int row, Graphics& g, int width, int height, bool rowIsSelected)
{
if (isPositiveAndBelow (row, items.size()))
{
if (rowIsSelected)
g.fillAll (findColour (TextEditor::highlightColourId)
.withMultipliedAlpha (0.3f));

            const String item (items [row]);
            bool enabled = false;

            AudioDeviceManager::AudioDeviceSetup config;
            setup.manager->getAudioDeviceSetup (config);

            if (setup.useStereoPairs)
            {
                if (type == audioInputType)
                    enabled = config.inputChannels [row * 2] || config.inputChannels [row * 2 + 1];
                else if (type == audioOutputType)
                    enabled = config.outputChannels [row * 2] || config.outputChannels [row * 2 + 1];
            }
            else
            {
                if (type == audioInputType)
                    enabled = config.inputChannels [row];
                else if (type == audioOutputType)
                    enabled = config.outputChannels [row];
            }

            const int x = getTickX();
            const float tickW = height * 0.75f;

            getLookAndFeel().drawTickBox (g, *this, x - tickW, (height - tickW) / 2, tickW, tickW,
                                          enabled, true, true, false);

            g.setFont (height * 0.6f);
            g.setColour (findColour (ListBox::textColourId, true).withMultipliedAlpha (enabled ? 1.0f : 0.6f));
            g.drawText (item, x, 0, width - x - 2, height, Justification::centredLeft, true);
        }
    }[/code]

from juce_AudioDeviceSelectorComponent.cpp file

If item is any ‘regular’ string like “Altavoz”, everything goes fine. But, if the string contains something like “Micr[color=#FF0000]ó[/color]fono” (4d 69 63 72 [color=#FF0000]f3[/color] 66 6f 6e 6f 20), such method crashes.

Gabriel

Huh…!?? Sorry, but that’s just ridiculous! The library has been happily drawing text with extended characters for many years. What’s the stack trace?

Here, the method crashes:
[attachment=1]pluginhost1.png[/attachment]

But here it works flawlessly:
[attachment=0]pluginhost0.png[/attachment]

Despite I can absolutely wrong, this comparison seems conclusive enough, isn’t it?

Gabriel

I don’t know what “stack-trace” is in spanish, but that’s the only thing that would be any help here.

OK Jules!

Here, they are the last messages from the result window:

[list][color=#0000FF]El subproceso 0xcec terminó con código 0 (0x0).
El subproceso 0x18d4 terminó con código 0 (0x0).
‘Plugin Host.exe’ (Win32): ‘C:\Windows\SysWOW64\d2d1.dll’ cargado. No se puede encontrar o abrir el archivo PDB.
‘Plugin Host.exe’ (Win32): ‘C:\Windows\SysWOW64\DWrite.dll’ cargado. No se puede encontrar o abrir el archivo PDB.
‘Plugin Host.exe’ (Win32): ‘C:\Windows\SysWOW64\mscms.dll’ cargado. No se puede encontrar o abrir el archivo PDB.
‘Plugin Host.exe’ (Win32): ‘C:\Windows\SysWOW64\userenv.dll’ cargado. No se puede encontrar o abrir el archivo PDB.
El subproceso 0x398 terminó con código 0 (0x0).
El subproceso 0x17f8 terminó con código 0 (0x0).
HEAP[Plugin Host.exe]: Heap block at 00B243D8 modified at 00B244B4 past requested size of d4
Plugin Host.exe ha desencadenado un punto de interrupción.
HEAP[Plugin Host.exe]: Invalid address specified to RtlValidateHeap( 00A40000, 00B243E0 )
Plugin Host.exe ha desencadenado un punto de interrupción.[/color]
[/list]
And here, it is the stack trace:

[list][color=#0000FF] ntdll.dll!77e7c067() Desconocido
[Los marcos siguientes pueden no ser correctos o faltar, no se han cargado símbolos para ntdll.dll]
ntdll.dll!77e31aec() Desconocido
ntdll.dll!77da4a79() Desconocido
KernelBase.dll!75767bbc() Desconocido

Plugin Host.exe!CrtIsValidHeapPointer(const void * pUserData=0x00b24400) Línea 2036 C++
Plugin Host.exe!free_dbg_nolock(void * pUserData=0x00b24400, int nBlockUse=1) Línea 1322 C++
Plugin Host.exe!free_dbg(void * pUserData=0x00b24400, int nBlockUse=1) Línea 1265 C++
Plugin Host.exe!operator delete(void * pUserData=0x00b24400) Línea 54 C++
Plugin Host.exe!operator delete[](void * p=0x00b24400) Línea 21 C++
Plugin Host.exe!juce::StringHolder::release(juce::StringHolder * const b=0x00b24400) Línea 164 C++
Plugin Host.exe!juce::StringHolder::release(const juce::CharPointer_UTF8 & text={…}) Línea 169 C++
Plugin Host.exe!juce::String::~String() Línea 253 C++
Plugin Host.exe!juce::AudioDeviceSettingsPanel::ChannelSelectorListBox::paintListBoxItem(int row=0, juce::Graphics & g={…}, int width=273, int height=22, bool rowIsSelected=false) Línea 762 C++
Plugin Host.exe!juce::ListBox::RowComponent::paint(juce::Graphics & g={…}) Línea 40 C++
Plugin Host.exe!juce::Component::paintComponentAndChildren(juce::Graphics & g={…}) Línea 1853 C++
Plugin Host.exe!juce::Component::paintEntireComponent(juce::Graphics & g={…}, bool ignoreAlphaLevel=false) Línea 1947 C++
Plugin Host.exe!juce::Component::paintWithinParentContext(juce::Graphics & g={…}) Línea 1836 C++
Plugin Host.exe!juce::Component::paintComponentAndChildren(juce::Graphics & g={…}) Línea 1899 C++
Plugin Host.exe!juce::Component::paintEntireComponent(juce::Graphics & g={…}, bool ignoreAlphaLevel=false) Línea 1947 C++
Plugin Host.exe!juce::Component::paintWithinParentContext(juce::Graphics & g={…}) Línea 1836 C++
Plugin Host.exe!juce::Component::paintComponentAndChildren(juce::Graphics & g={…}) Línea 1899 C++
Plugin Host.exe!juce::Component::paintEntireComponent(juce::Graphics & g={…}, bool ignoreAlphaLevel=false) Línea 1947 C++
Plugin Host.exe!juce::Component::paintWithinParentContext(juce::Graphics & g={…}) Línea 1836 C++
Plugin Host.exe!juce::Component::paintComponentAndChildren(juce::Graphics & g={…}) Línea 1899 C++
Plugin Host.exe!juce::Component::paintEntireComponent(juce::Graphics & g={…}, bool ignoreAlphaLevel=false) Línea 1947 C++
Plugin Host.exe!juce::Component::paintWithinParentContext(juce::Graphics & g={…}) Línea 1836 C++
Plugin Host.exe!juce::Component::paintComponentAndChildren(juce::Graphics & g={…}) Línea 1899 C++
Plugin Host.exe!juce::Component::paintEntireComponent(juce::Graphics & g={…}, bool ignoreAlphaLevel=false) Línea 1947 C++
Plugin Host.exe!juce::Component::paintWithinParentContext(juce::Graphics & g={…}) Línea 1836 C++
Plugin Host.exe!juce::Component::paintComponentAndChildren(juce::Graphics & g={…}) Línea 1899 C++
Plugin Host.exe!juce::Component::paintEntireComponent(juce::Graphics & g={…}, bool ignoreAlphaLevel=false) Línea 1947 C++
Plugin Host.exe!juce::Component::paintWithinParentContext(juce::Graphics & g={…}) Línea 1836 C++
Plugin Host.exe!juce::Component::paintComponentAndChildren(juce::Graphics & g={…}) Línea 1899 C++
Plugin Host.exe!juce::Component::paintEntireComponent(juce::Graphics & g={…}, bool ignoreAlphaLevel=false) Línea 1947 C++
Plugin Host.exe!juce::Component::paintWithinParentContext(juce::Graphics & g={…}) Línea 1836 C++
Plugin Host.exe!juce::Component::paintComponentAndChildren(juce::Graphics & g={…}) Línea 1899 C++
Plugin Host.exe!juce::Component::paintEntireComponent(juce::Graphics & g={…}, bool ignoreAlphaLevel=true) Línea 1947 C++
Plugin Host.exe!juce::ComponentPeer::handlePaint(juce::LowLevelGraphicsContext & contextToPaintTo={…}) Línea 133 C++
Plugin Host.exe!juce::HWNDComponentPeer::handlePaintMessage() Línea 1508 C++
Plugin Host.exe!juce::HWNDComponentPeer::peerWindowProc(HWND
* h=0x00120322, unsigned int message=15, unsigned int wParam=0, long lParam=0) Línea 2175 C++
Plugin Host.exe!juce::HWNDComponentPeer::windowProc(HWND
_ * h=0x00120322, unsigned int message=15, unsigned int wParam=0, long lParam=0) Línea 2129 C++
user32.dll!75cc77d8() Desconocido
user32.dll!75cc90e7() Desconocido
user32.dll!75cc787a() Desconocido
user32.dll!75cc7b6f() Desconocido
user32.dll!75cc7c44() Desconocido
ntdll.dll!77de2f02() Desconocido
user32.dll!75ccb8d8() Desconocido
user32.dll!75cc8a66() Desconocido
Plugin Host.exe!juce::MessageManager::dispatchNextMessageOnSystemQueue(bool returnIfNoPendingMessages=true) Línea 137 C++
Plugin Host.exe!juce::MessageManager::runDispatchLoopUntil(int millisecondsToRunFor=20) Línea 119 C++
Plugin Host.exe!juce::ModalComponentManager::runEventLoopForCurrentComponent() Línea 272 C++
Plugin Host.exe!juce::Component::runModalLoop() Línea 1601 C++
Plugin Host.exe!juce::DialogWindow::LaunchOptions::runModal() Línea 121 C++
Plugin Host.exe!MainHostWindow::showAudioSettings() Línea 429 C++
Plugin Host.exe!MainHostWindow::perform(const juce::ApplicationCommandTarget::InvocationInfo & info={…}) Línea 396 C++
Plugin Host.exe!juce::ApplicationCommandTarget::tryToInvoke(const juce::ApplicationCommandTarget::InvocationInfo & info={…}, bool async=false) Línea 69 C++
Plugin Host.exe!juce::ApplicationCommandTarget::invoke(const juce::ApplicationCommandTarget::InvocationInfo & info={…}, const bool async=false) Línea 147 C++
Plugin Host.exe!juce::ApplicationCommandManager::invoke(const juce::ApplicationCommandTarget::InvocationInfo & inf={…}, bool asynchronously=false) Línea 188 C++
Plugin Host.exe!juce::KeyPressMappingSet::invokeCommand(const int commandID=197120, const juce::KeyPress & key={…}, const bool isKeyDown=true, const int millisecsSinceKeyPressed=0, juce::Component * originatingComponent=0x00a5aab0) Línea 211 C++
Plugin Host.exe!juce::KeyPressMappingSet::keyPressed(const juce::KeyPress & key={…}, juce::Component * originatingComponent=0x00a5aab0) Línea 335 C++
Plugin Host.exe!juce::ComponentPeer::handleKeyPress(int keyCode=65, unsigned int textCharacter=0) Línea 167 C++
Plugin Host.exe!juce::HWNDComponentPeer::doKeyChar(int key=65, const long flags=1966081) Línea 1940 C++
Plugin Host.exe!juce::HWNDComponentPeer::peerWindowProc(HWND__ * h=0x000f059e, unsigned int message=258, unsigned int wParam=1, long lParam=1966081) Línea 2261 C++
Plugin Host.exe!juce::HWNDComponentPeer::windowProc(HWND__ * h=0x000f059e, unsigned int message=258, unsigned int wParam=1, long lParam=1966081) Línea 2129 C++
user32.dll!75cc77d8() Desconocido
user32.dll!75cc78cb() Desconocido
user32.dll!75cc9431() Desconocido
user32.dll!75cc787a() Desconocido
user32.dll!75cc899d() Desconocido
user32.dll!75cc8a66() Desconocido
Plugin Host.exe!juce::MessageManager::dispatchNextMessageOnSystemQueue(bool returnIfNoPendingMessages=false) Línea 137 C++
Plugin Host.exe!juce::MessageManager::runDispatchLoopUntil(int millisecondsToRunFor=-1) Línea 119 C++
Plugin Host.exe!juce::MessageManager::runDispatchLoop() Línea 100 C++
Plugin Host.exe!juce::JUCEApplication::main() Línea 246 C++
Plugin Host.exe!WinMain(void * __formal=0x01240000, void * __formal=0x00000000, const char * __formal=0x00a44448, int __formal=10) Línea 107 C++
Plugin Host.exe!__tmainCRTStartup() Línea 238 C
Plugin Host.exe!WinMainCRTStartup() Línea 164 C
kernel32.dll!77ba8543() Desconocido
ntdll.dll!77dfac69() Desconocido
ntdll.dll!77dfac3c() Desconocido[/color][/list]

You’ve got some memory corruption going on around that string. But just to make things clear, the problem is completely unrelated to any component or graphics code, or even the String class - those things are just where you’re seeing the effects of the memory being damaged. Like I said in my first post, check the place where that string is first copied from the audio driver.

Dear Jules,

Sorry to be obstinate with this topic. The String item is not corrupt in the origin. I’ve made the following test:

In a well-formed item String that works ok, I’ve changed a standard ASCII character (0x6f, this is ‘o’) to one extended f3 (character ‘ó’) just before the method

[color=#00BF00]g.drawText (item, x, 0, width - x - 2, height, Justification :: centredLeft, true);[/color]

is called and it crashed.

Certainly I do not know if such method expects a plain ASCII String, an extended ASCII set with ISO 8859-1, ISO Also called Latin-1, or an UTF8 format. But g.drawText does not digest well extended set ISO 8859-1.

Dare I suggest that you try to inject an item String in g.drawText with an extended ASCII character.

Gabriel

Can you put this test program into a GitHub repository that anyone can pull and compile? Make sure to include the JUCE sources in the repository along with the project files (especially Visual Studio 2010).