Categories
discuss

Virtual synthesizer in Android (based on jar) – native jni?

I am trying to load & use virtual (soft) synthesizer of java in Android. I read Gervill jar is to be used for this. Other building block is javax-sound present in my project as an aar file (as is it not present in trimmed Java package of Android)

Gradle file inclusions are

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.0"
    dataBinding {
        enabled = true
    }
    defaultConfig {
        applicationId "com.mmm.ttt"
        minSdkVersion 18
        targetSdkVersion 29
        multiDexEnabled true
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation project(path: ':javax-sound')
    implementation project(path: ':gervilljar')         // This is folder name at project root folder level containing gervill.jar
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'com.android.support:multidex:1.0.3'
}

Problem areas of code

Code 1

MidiSystem.getMidiDeviceInfo().size    //results in an empty array in all cases

Some answers suggest only inclusion of gervill jar is enough for auto detection. But since that hasnt worked for me, I written this

Code 2

var synth = SoftSynthesizer() as Synthesizer
        MidiSystem.addSynthesizer(synth)

I get compilation error for this

Supertypes of the following classes cannot be resolved. Please make sure you have the required dependencies in the classpath:
    class com.sun.media.sound.SoftSynthesizer, unresolved supertypes: com.sun.media.sound.ReferenceCountingDevice
    class com.sun.media.sound.AudioSynthesizer, unresolved supertypes: javax.sound.midi.Synthesizer 

Code 3

var synth = MidiSystem.getSynthesizer()
synth?.open()

With inclusion to gradle as above, this is the run time error in absence of code 2

Caused by: jp.kshoji.javax.sound.midi.MidiUnavailableException: Synthesizer not found
        at jp.kshoji.javax.sound.midi.MidiSystem.getSynthesizer(MidiSystem.java:323)

I followed https://stackoverflow.com/a/38749847/1029110 for adding aar/jar files
But, havent linked each other in the module dependency gradle (Should/How to do that?) although gervill needs javax.sound

Final aim is to load & play soundfont files. Avoiding native jni code if possible.

EDIT

1 problem identified. Gervill.jar is not good enough to work error free. So I a picked few other classes from com.sun.media.sound.* and compiled into new jar. Now that error in code 2 (unresolved supertypes) has gone away.

Next trial was to include Android midi service

val midiManager = context.getSystemService(Context.MIDI_SERVICE) as android.media.midi.MidiManager
midiManager.getDevices()

This gave 2 services depending on the external applications installed on mobile. But they are not linked to the softsynthesizer in anyway.  
So, Code 1 still has empty array for MidiDeviceInfo.  
Part of Code 3: synth?.open() still failing badly, but with newer error. (Currently stuck here)

Caused by: jp.kshoji.javax.sound.midi.MidiUnavailableException: Can not open line
        at com.sun.media.sound.SoftSynthesizer.open(SoftSynthesizer.java:1132)
        at com.sun.media.sound.SoftSynthesizer.open(SoftSynthesizer.java:1036)
Caused by: java.lang.IllegalArgumentException: No line matching interface jp.kshoji.SourceDataLine supporting format PCM_SIGNED 44100.0 Hz, 16 bit, stereo, 4 bytes/frame, little-endian is supported.
        at jp.kshoji.javax.sound.sampled.AudioSystem.getLine(AudioSystem.java:475)
        at jp.kshoji.javax.sound.sampled.AudioSystem.getSourceDataLine(AudioSystem.java:602)
        at com.sun.media.sound.SoftSynthesizer.open(SoftSynthesizer.java:1066)
        at com.sun.media.sound.SoftSynthesizer.open(SoftSynthesizer.java:1036) 

Code 4:

jp.kshoji.javax.sound.midi.MidiSystem.getSoundbank(File("soundfont.sf2"))

Fails with

Caused by: java.lang.UnsupportedOperationException: not implemented.
    at jp.kshoji.javax.sound.midi.MidiSystem.getSoundbank(MidiSystem.java:277)

I have checked the Java for windows version source code. There is no override or implementation of the file to use getSoundbank.

Fixes & future steps?

Answer

Solutions in my understanding

Code 3.

github.com/KyoSherlock/MidiDriver is the android ready code which has same contents of com.sun.media.sound & jpkshojijavaxsoundmidi put together. Also trimmed to exclude unwanted classes.

Other midi drivers like

are based on native C/C++ code, which I avoided all along.

This answer helped https://stackoverflow.com/a/56700883/1029110

Code 4.

Call to load soundbank should be done via SF2SoundBank & not via MidiSystem.getSoundbank(soundFontFile)

val sf = SF2Soundbank(assets.open("SmallTimGM6mb.sf2"))

Code 1.

Might not be important for producing sounds via phone speaker, as even the KyoSherlock running sample returns 0

In depth reading can be done here http://jsresources.sourceforge.net/faq_midi.html

Categories
discuss

MutableLiveData not updating in UI

UPDATE: If i move to another fragment and return to this one the TextView gets updated…

I am unable to get the MutableLiveData in the UI to update to a new value either by using setValue() or postValue(). I can get this to work by changing the MutableLiveData to ObservableData but the point is to use LiveData not ObservableData.

I have similar things working in other views without an issue. Unsure whats happening here… The only thing I can think of is that I am jumping back and forth between a camera activity (via intent) and this fragment. On the activity result I am setting data from the fragment to the ViewModel.

I dont believe that I need to set any observer for the value in the fragment since i have 2 way databinding between the XML and ViewModel.

my_fragment.XML

<?xml version="1.0" encoding="utf-8"?>

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="myViewModel"
            type="com.example.myapplication.MyViewModel" />
    </data>

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

            <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@={myViewModel.photosCount}"
            tools:text="1" />

           <Button
               android:id="@+id/btnTakePhoto"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="Take Picture"
               android:onClick="@{myViewModel::navClicked}"/>

    </LinearLayout>

</layout>

MyViewModel.java

private MutableLiveData<String> photosCount = new MutableLiveData<>();
private MutableLiveData<Boolean> takePhoto = new MutableLiveData<>();

public MyViewModel() {
    photosCount.setValue("0"); // This correctly sets the value to "0" in UI
    takePhoto.setValue(true);
}

public MutableLiveData<String> getPhotosCount() {
    return photosCount;
}

public MutableLiveData<Boolean> getTakePhoto() {
    return takePhoto;
}

public void storeImage() {
    ....
    Log.d(TAG, "Updating UI count to: " + String.valueOf(count)); // this shows the correct value that should be updated in the UI
    photosCount.setValue(String.valueOf(count));
    Log.d(TAG, "Updated value: " + photosCount.getValue()); // this shows the correct updated value
    ...
}

public void navClicked(@NonNull View view) {
    if (view.getId() == R.id.btnTakePhoto) {
        takePhoto.setValue(true);
    }
}

Now, since LiveData does not guarantee updating the UI at the time of changing the value I thought this might be a binding dispatch issue. That hasent resolved the issue either…

MyFragment.java

private MyViewModel myViewModel;
MyFragmentBinding binding;

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    super.onCreateView(inflater, container, savedInstanceState);

    myViewModel = new ViewModelProvider(this).get(MyViewModel.class);

    binding = DataBindingUtil.inflate(inflater, R.layout.my_fragment, container, false);
    binding.setMyViewModel(myViewModel);

    return binding.getRoot();
}

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    viewModel.getTakePhoto().observe(getViewLifecycleOwner(), takePhoto -> {
        if (takePhoto) {
            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

            if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
                File photo = viewModel.createImageFile();
                if (photo != null) {
                    Uri photoURI = FileProvider.getUriForFile(getActivity(),"com.example.fileprovider", photo);
                    intent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
                    this.startActivityForResult(intent, Constants.RequestCodes.MY_REQUEST_IMAGE_CAPTURE);
                }
            }
        }
    });
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == Activity.RESULT_OK) {
        if (requestCode == Constants.RequestCodes.MY_REQUEST_IMAGE_CAPTURE) {
            viewModel.storeImage();
        }
    }
}

Again, If you switch photosCount from MutableLiveData to ObservableData the problem is fixed however this is not LiveData.

Answer

I am unable to get the MutableLiveData in the UI to update to a new value either by using setValue() or postValue(). I can get this to work by changing the MutableLiveData to ObservableData but the point is to use LiveData not ObservableData.

You probably don’t set a lifecycle owner for your binding object. From the JAVADOC for setLifecycleOwner() this assumption sounds even more convincing (please see it here):

If a LiveData is in one of the binding expressions and no LifecycleOwner is set, the LiveData will not be observed and updates to it will not be propagated to the UI.

So, I took a closer look at your code.

Observation

I noticed that you don’t show how your ViewModel and binding are created. So, I have created a simple project meeting your requirements and updating the UI properly – setValue or postValue of LiveData were changing the corresponding View values seamlessly. Then, I filled in the missing pieces in your code based on my working example. Please see the updated code below.

Relevant Code

MainViewModel

public class MainViewModel extends ViewModel {
    private MutableLiveData<String> photosCount = new MutableLiveData<>();
    private MutableLiveData<Boolean> takePhoto = new MutableLiveData<>();

    public MainViewModel() {
        photosCount.setValue("0");
        takePhoto.setValue(true);
    }

    public MutableLiveData<String> getPhotosCount() {
        return photosCount;
    }

    public MutableLiveData<Boolean> getTakePhoto() {
        return takePhoto;
    }

    public void createImageFile() {
        ...
        photosCount.setValue(String.valueOf(count));
        ...
    }
    ...
}

fragment_main.xml

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="viewModel"
            type="MainViewModel"/>
    </data>

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@={viewModel.photosCount}" />
    </LinearLayout>
</layout>

MainFragment

public class MainFragment extends Fragment {
    private MainViewModel mainViewModel;
    private FragmentMainBinding binding;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_main, container,
                false);
        binding.setLifecycleOwner(this);

        mainViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
        binding.setViewModel(mainViewModel);

        return binding.getRoot();
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        mainViewModel.getTakePhoto().observe(getViewLifecycleOwner(), takePhoto -> {
            if (takePhoto) {
                Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

                if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
                    File photo = mainViewModel.storeImage();
                    if (photo != null) {
                        Uri photoURI = FileProvider.getUriForFile(getActivity(),"com.example.fileprovider", photo);
                        intent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
                        this.startActivityForResult(intent, 0);
                    }
                }
            }
        });
        binding.executePendingBindings();
    }
}

Conclusion

Please note that a lifecycle owner must be set for binding as binding.setLifecycleOwner(this). MainViewModel must be set for binding too. Otherwise, it won’t work properly. When in doubt, always check the official documentation here.

Categories
discuss

How to Convert hashmap into proper json format in mule 4?

I am trying to log my hashmap it looks something like this : enter image description here

How do I convert it into proper json format ?

Answer

You can use the Transform Component and write the dataweave code as follows:

%dw 2.0
output application/json
---
payload pluck(message,property,index) -> {(index) :{(property) :message}}

This should work.

Categories
discuss

How to use mockito to mock grpc ServiceBlockingStub to throw StatusRuntimeException(Status.UNAVAILABLE)?

I want to mock my grpc client to ensure that it is resilient to failure by throwing an new StatusRuntimeException(Status.UNAVAILABLE) (This is the exception that is thrown when java.net.ConnectException: Connection refused is thrown to the grpc client). However, the generated class is final, so mock will not work.

How do I get BlahServiceBlockingStub to throw new StatusRuntimeException(Status.UNAVAILABLE) without having to refactor my code to create a wrapper class around BlahServiceBlockingStub?

This is what I have tried (where BlahServiceBlockingStub was generated by grpc):

    @Test
    public void test() {
        BlahServiceBlockingStub blahServiceBlockingStub = mock(BlahServiceBlockingStub.class);

        when(blahServiceBlockingStub.blah(any())).thenThrow(new StatusRuntimeException(Status.UNAVAILABLE));


        blahServiceBlockingStub.blah(null);
    }

Unfortunately I get the below exception as expected:

org.mockito.exceptions.base.MockitoException: 
Cannot mock/spy class BlahServiceGrpc$BlahServiceBlockingStub
Mockito cannot mock/spy following:
  - final classes
  - anonymous classes
  - primitive types

    at MyTestClass.test(MyTestClass.java:655)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
.
.
.

Because I tried mocking the final class generated by grpc:

  public static final class BlahServiceBlockingStub extends io.grpc.stub.AbstractStub<BlahServiceBlockingStub> {
    private BlahServiceBlockingStub(io.grpc.Channel channel) {
      super(channel);
    }

Answer

Do not mock the client stub, or any other final class/method. The gRPC team may go out of their way to break your usage of such mocks, as they are extremely brittle and can produce “impossible” results.

Mock the service, not the client stub. When combined with the in-process transport it produces fast, reliable tests. This is the same approach as demonstrated in the grpc-java hello world example.

@Rule
public final GrpcCleanupRule grpcCleanup = new GrpcCleanupRule();

@Test
public void test() {
    // This can be a mock, but is easier here as a fake implementation
    BlahServiceImplBase serviceImpl = new BlahServiceImplBase() {
        @Override public void blah(Request req, StreamObserver<Response> resp) {
            resp.onError(new StatusRuntimeException(Status.UNAVAILABLE));
        }
    };
    // Note that the channel and server can be created in any order
    grpcCleanup.register(InProcessServerBuilder.forName("mytest")
        .directExecutor().addService(serviceImpl).build().start());
    ManagedChannel chan = grpcCleanup.register(
        InProcessChannelBuilder.forName("mytest").directExecutor().build();
    BlahServiceBlockingStub blahServiceBlockingStub
        = BlahServiceGrpc.newBlockingStub();

    blahServiceBlockingStub.blah(null);
}

When doing multiple tests, you can hoist the server, channel, and stub creation into fields or @Before, out of the individual tests. When doing that it can be convenient to use MutableHandlerRegistry as a fallbackHandlerRegistry() on the server. That allows you to register services after the server is started. See the route guide example for a fuller example of that approach.

Categories
discuss

Spring Boot MVC Test – MockMvc is always null

I’m trying to write my first Spring MVC test but I just cannot get Spring Boot to inject the MockMvc dependency into my test class. Here is my class:

@WebMvcTest
public class WhyWontThisWorkTest {

  private static final String myUri = "uri";
  private static final String jsonFileName = "myRequestBody.json";

  @Autowired
  private MockMvc mockMvc;

  @Test
  public void iMustBeMissingSomething() throws Exception {
    byte[] jsonFile = Files.readAllBytes(Paths.get("src/test/resources/" + jsonFileName));
    mockMvc.perform(
        MockMvcRequestBuilders.post(myUri)
            .content(jsonFile)
            .contentType(MediaType.APPLICATION_JSON))
        .andExpect(MockMvcResultMatchers.status().is2xxSuccessful());
  }
}

I’ve checked with IntelliJ’s debugger and can confirm that mockMvc itself is null. Thus, all the Exception message tells me is “java.lang.NullPointerException”.

I’ve already tried adding more general Spring Boot annotations for test classes like “@SpringBootTest” or “@RunWith(SpringRunner.class)” in case it has something to do with initializing Spring but no luck.

Answer

Strange, provided that you have also tried with @RunWith(SpringRunner.class) and @SpringBootTest. Have you also tried with the @AutoConfigureMockMvc annotation? The sample below is working fine.

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class HelloControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void getHello() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().string(equalTo("Hello World of Spring Boot")));
    }
}

complete sample here

It may also be worthwhile to consider the following comments regarding the usage of the @WebMvcTest and @AutoConfigureMockMvc annotations as detailed in Spring’s documentation

By default, tests annotated with @WebMvcTest will also auto-configure Spring Security and MockMvc (include support for HtmlUnit WebClient and Selenium WebDriver). For more fine-grained control of MockMVC the @AutoConfigureMockMvc annotation can be used.

Typically @WebMvcTest is used in combination with @MockBean or @Import to create any collaborators required by your @Controller beans.

If you are looking to load your full application configuration and use MockMVC, you should consider @SpringBootTest combined with @AutoConfigureMockMvc rather than this annotation.

When using JUnit 4, this annotation should be used in combination with @RunWith(SpringRunner.class).

Source: stackoverflow
Text is available under the Creative Commons Attribution-ShareAlike License; additional terms may apply. By using this site, you agree to the Privacy Policy, and Copyright Policy. Content is available under CC BY-SA 3.0 unless otherwise noted. The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 © No Copyrights, All Questions are retrived from public domain..