playTune is called whenever a user selects a button. The problem is the line myButton.setPressed(true); is only called after the tune is played. This defies logic, so I'm wondering whether Android lines up events in an queue? I would like the line myButton.setPressed(true); to be called as it appears in the code. i.e. before a note is played.
private void playTune() {
isTunePlaying = true;
//Get no of notes user selected
String selectedValue = noteCountValues[noteCountIdx];
Log.v(LOG_TAG, "selectedValue:"+selectedValue+" noteCountIdx:"+noteCountIdx);
int noOfNotes;
if ("ALL".equals(selectedValue)){
noOfNotes = 50;
}
else{
noOfNotes = Integer.parseInt(selectedValue);
}
TuneManager tuneManager = TuneManager.getInstance(this);
Log.v(LOG_TAG, "tuneNamesIdx:"+tuneNamesIdx);
Tune tune = tuneManager.getTune(tuneNamesIdx+1);
Log.v(LOG_TAG, " tuneTitle:"+tune.getTitle()+" tuneNoOfNotes:"+tune.getNotes().size());
//Initialise expectedNotes
expectedNotes = new StringBuffer();
//Get notes and play
List<Note> notes = tune.getNotes();
//for (Note note:tune.getNotes()){
for (int i=1; i<=notes.size() && i<=noOfNotes; i++){
Log.v(LOG_TAG, "i:"+i+" notesSize:"+notes.size()+" noOfNotes:"+noOfNotes);
//Highlight note
if (isHighlightNotesOn){
final View myButton = (ImageButton) findViewById(R.id.middle_c);
myButton.setPressed(true);
}
Note note = notes.get(i-1);
int notePos = soundManager.getNotePosition(note);
Log.v(LOG_TAG, "current note:"+note.getName()+" playing notePos:"+notePos+" setting duration:"+note.getDurationMS());
soundManager.playSound(notePos);
//Add to expectedNotes
expectedNotes.append(" "+note.getName()+",");
//Sleep for the duration
try{
Thread.currentThread().sleep(note.getDurationMS());//sleep for 1000 ms
}
catch(InterruptedException ie){
}
}
isTunePlaying = false;
//Initialise actualNotesPlayed i.e. start from after the tine has finished
actualNotesPlayed = new StringBuffer();
}
Not sure if this is what you are running into, but if you cause an event from something event driven (happening on the UI thread) it of course cannot be processed on the UI thread until after your current event handler returns.
Related
I am looking for ideas on how to handle envelope re-triggering of new notes in a monophonic sampler setup causing clicks if the previous note's envelope hasn't finished. In the current setup the previous note's instance is killed on the spot when a new note is triggered (the synth.stop method call), causing a click as the envelope doesn't get a chance to finish and reach 0 volume. Any hints are welcome.
I have also added in the below code my own un-satisfactory solution putting the gain of the voice to 0 and then putting the voice to sleep for 70ms. This introduces a 70ms latency to the user interaction but gets rid of any clicks. Any values below 70ms in the sleep don't solve the clicking.
The variable are public static at the moment just so I can still play around with where I'm calling them.
Here is my listener code:
buttonNoteC1Get.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
buttonNoteC1Get.setBackgroundColor(myColorWhite); // reset gui color
if (sample.getSustainBegin() > 0) { // trigger release for looping sample
ampEnv.dataQueue.queue(ampEnvelope, 3, 1); // release called
}
limit = 0; // reset action down limiter
return true;
}
if (limit == 0) { // respond only to first touch event
if (samplePlayer != null) { // check if a previous note exists
synth.stop(); // stop instance of previous note
}
buttonNoteC1Get.setBackgroundColor(myColorGrey); // key pressed gui color
samplePitch = octave * 1; // set samplerate multiplier
Sampler.player(); // call setup code for new note
Sampler.play(); // play new note
limit = 1; // prevent stacking of action down touch events
}
return false;
}
}); // end listener
Here is my Sampler code
public class Sampler {
public static VariableRateDataReader samplePlayer;
public static LineOut lineOut;
public static FloatSample sample;
public static SegmentedEnvelope ampEnvelope;
public static VariableRateMonoReader ampEnv;
public static MixerMonoRamped mixerMono;
public static double[] ampData;
public static FilterStateVariable mMainFilter;
public static Synthesizer synth = JSyn.createSynthesizer(new JSynAndroidAudioDevice());
// load the chosen sample, called by instrument select spinner
static void loadSample(){
SampleLoader.setJavaSoundPreferred(false);
try {
sample = SampleLoader.loadFloatSample(sampleFile);
} catch (IOException e) {
e.printStackTrace();
}
} // end load sample
// initialize sampler voice
static void player() {
// Create an amplitude envelope and fill it with data.
ampData = new double[] {
envA, 0.9, // pair 0, "attack"
envD, envS, // pair 2, "decay"
0, envS, // pair 3, "sustain"
envR, 0.0, // pair 4, "release"
/* 0.04, 0.0 // pair 5, "silence"*/
};
// initialize voice
ampEnvelope = new SegmentedEnvelope(ampData);
synth.add(ampEnv = new VariableRateMonoReader());
synth.add(lineOut = new LineOut());
synth.add(mixerMono = new MixerMonoRamped(2));
synth.add(mMainFilter = new FilterStateVariable());
// connect signal flow
mixerMono.output.connect(mMainFilter.input);
mMainFilter.output.connect(0, lineOut.input, 0);
mMainFilter.output.connect(0, lineOut.input, 1);
// set control values
mixerMono.amplitude.set(sliderVal / 100.0f);
mMainFilter.amplitude.set(0.9);
mMainFilter.frequency.set(mainFilterCutFloat);
mMainFilter.resonance.set(mainFilterResFloat);
// initialize and connect sampler voice
if (sample.getChannelsPerFrame() == 1) {
synth.add(samplePlayer = new VariableRateMonoReader());
ampEnv.output.connect(samplePlayer.amplitude);
samplePlayer.output.connect(0, mixerMono.input, 0);
samplePlayer.output.connect(0, mixerMono.input, 1);
} else if (sample.getChannelsPerFrame() == 2) {
synth.add(samplePlayer = new VariableRateStereoReader());
ampEnv.output.connect(samplePlayer.amplitude);
samplePlayer.output.connect(0, mixerMono.input, 0);
samplePlayer.output.connect(1, mixerMono.input, 1);
} else {
throw new RuntimeException("Can only play mono or stereo samples.");
}
} // end player
// play the sample
public static void play() {
if (samplePlayer != null)
{samplePlayer.dataQueue.clear();
samplePlayer.rate.set(sample.getFrameRate() * samplePitch); // set pitch
}
// start the synth engine
synth.start();
lineOut.start();
ampEnv.start();
// play one shot sample
if (sample.getSustainBegin() < 0) {
samplePlayer.dataQueue.queue(sample);
ampEnv.dataQueue.queue( ampEnvelope );
// play sustaining sample
} else {
samplePlayer.dataQueue.queueOn(sample);
ampEnv.dataQueue.queue( ampEnvelope, 0,3);
ampEnv.dataQueue.queueLoop( ampEnvelope, 1, 2 );
}
} }
Unsatisfactory solution that introduces 70ms of latency, changing the action down listener handling of a previous note to this:
if (limit == 0) {
if (samplePlayer != null) {
mixerMono.amplitude.set(0);
try {
synth.sleepFor(0.07);
synth.stop(); // stop instance of previous note
}catch (InterruptedException e) {
e.printStackTrace();
}
}
You should not call synth.start() and synth.stop() for every note. Think of it like powering on a physical synthesizer. Just start the synth and the lineOut once. If the ampEnv is connected indirectly to something else that is start()ed then you do not need to start() the ampEnv.
Then just queue your samples and envelopes when you want to start a note.
When you are all done playing notes then call synth.stop().
I am doing a mental calculation game and it is the following the game consume in has the accounts to do and the user has to write with the keyboard in android the number until then I have now what I want to do is when I pass from level to Textinput is soon selected so that the users can only enter the numbers (I am also in doubt as to put only the numerical keyboard for andorid keyboards).
This is the game code:
conta.restrict = "0-9\n";
conta.maxChars = 3;
conta.background = true;
conta.backgroundColor = 0x4DFF67;
conta.border = true; // Terá borda
conta.borderColor = 0xFFCD70; // Cor da borda
conta.text = "Nº";
vala.addEventListener(MouseEvent.CLICK, tobias);
function tobias(MouseEvent){
if (conta.text == "12"){
trace("Acertou");
nextFrame();
}else{
trace("Errou");
conta.text = "";
conta.backgroundColor = 0xFF4D40;
}
}
conta.addEventListener(KeyboardEvent.KEY_DOWN, function(e:KeyboardEvent):void
{
if(e.keyCode == Keyboard.ENTER)
{
if (conta.text == "12"){
trace("Acertou");
nextFrame();
}else{
trace("Errou");
conta.text = "";
conta.backgroundColor = 0xFF4D40;
}
}
});
conta.addEventListener(KeyboardEvent.KEY_UP, function(e:KeyboardEvent):void
{
if(e.keyCode == Keyboard.ENTER)
{
conta.text = "";
}
});
conta.addEventListener(FocusEvent.FOCUS_IN, clearBox);
function clearBox(evt:FocusEvent):void
{
conta.text="";
}
Text Input
First of all sorry to repost the question as I didn't post my code earlier just written some lines now I am failing to explain.So posting the question again with codes.
I have written a code that will log the acceleromter data and save the data in a csv file. What I need to do is I have to run the acceleromter in background as a service. So,I m done with writing the codes for
1.running the accelerometer,
2.shown the data in textbox,
3.write the data in a csv file.
Things that are left is making it a service. I m giving the code below(Howfar I have done)
#SuppressWarnings("deprecation")
public class AccelerometerVaue extends Activity implements SensorListener {
SensorManager sm = null;
TextView xacc= null;
TextView yacc = null;
TextView zacc = null;
TextView xorient = null;
TextView yorient = null;
TextView zorient = null;
TextView text = null;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sm = (SensorManager) getSystemService(SENSOR_SERVICE);
setContentView(R.layout.activity_main);
xacc = (TextView) findViewById(R.id.xvalue);
yacc = (TextView) findViewById(R.id.yvalue);
zacc = (TextView) findViewById(R.id.zvalue);
xorient = (TextView) findViewById(R.id.xvalues);
yorient = (TextView) findViewById(R.id.yvalues);
zorient = (TextView) findViewById(R.id.zvalues);
}
#SuppressLint("SdCardPath")
public void onSensorChanged(int sensor, float[] values) {
synchronized (this) {
if (sensor == SensorManager.SENSOR_ORIENTATION) {
xorient.setText("Orientation X: " + values[0]);
yorient.setText("Orientation Y: " + values[1]);
zorient.setText("Orientation Z: " + values[2]);
}
if (sensor == SensorManager.SENSOR_ACCELEROMETER) {
// Time now = new Time();
// now.setToNow();
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
String currentDateandTime = sdf.format(new Date());
Log.d("Time",currentDateandTime);
xacc.setText("Accel X: " + values[0]);
yacc.setText("Accel Y: " + values[1]);
zacc.setText("Accel Z: " + values[2]);
String res=String.valueOf(currentDateandTime+"#"+values[0])+"#"+String.valueOf(values[1])+"#"+String.valueOf(values[2]);
Log.d("test", res);
CSVWriter writer = null;
try
{
//Log.d("check","pasla");
//Environment.getExternalStorageDirectory().getPath();
writer = new CSVWriter(new FileWriter("/sdcard/AccelData.csv",true), ',');
String[] entries = res.split("#"); // array of your values
writer.writeNext(entries);
//FileWriter
writer.close();
}
catch (IOException e)
{
//error
}
}
}
}
public void onAccuracyChanged(int sensor, int accuracy) {
String tag = null;
Log.e(tag,"onAccuracyChanged: " + sensor + ", accuracy: " + accuracy);
}
#Override
protected void onResume() {
super.onResume();
sm.registerListener(this,
SensorManager.SENSOR_ORIENTATION |
SensorManager.SENSOR_ACCELEROMETER,
SensorManager.SENSOR_DELAY_NORMAL);
}
#Override
protected void onStop() {
sm.unregisterListener(this);
super.onStop();
}
}
So, now I need to run a background service which will keep taking accelerometer data in background and also keep saving into that csv file. How can I do this? One more I like to ask is it possible to take the acc data after a certain period of time? Suppose here I m logging all the data continuously which is huge and thew battery drain will be huge. So, I need to implemented the service in such a way so that it will run in background for 15mins and log the data for 10 seconds at the beginning of each minute.
I have just replied to a similar question.
You can use Android's scheduling mechanism, called AlarmManager, to schedule your services.
There is a problem inerent to this approach, though: when the system is in battery-saving sleep state, your service has to acquire a wake lock in order to execute properly, then when finished release the wake lock for the system to go back to sleep state. Implementing this wake lock acquisition/release mechanism is not a trivial task.
I recommend using Commonsware's WakefulIntentService in order to avoid writing your own wake lock acquisition/release mechanism for your service. It's very easy to use. You will basically declare a class that extends the WakefulIntentService class and override its doWakefulWork() method. In that method you will poll for accelerometer data and write it to a file. I'll just let you down in the part of displaying the data in the screen, I don't know how to do that (I suppose you will have to perform a bind to the service though). <:)
How can I get programatically the elapsed time since the mobile data connection was activated?
I tried to do something like this:
phone = PhoneFactory.getDefaultPhone();
List<DataConnection> dcs = phone.getCurrentDataConnectionList();
for (DataConnection dc : dcs) {
if (dc.isActive()) {
timeElapsed = (System.currentTimeMillis() - dc.getConnectionTime())/1000;
}
}
but those methods are not visible from the SDK, so I tried Reflection:
try {
Class mPhoneFactory = Class.forName("com.android.internal.telephony.PhoneFactory");
Method mMakeDefaultPhone = mPhoneFactory.getMethod("makeDefaultPhone", new Class[] {Context.class});
mMakeDefaultPhone.invoke(null, this);
Method mGetDefaultPhone = mPhoneFactory.getMethod("getDefaultPhone", null);
Object mPhone = mGetDefaultPhone.invoke(null);
Method mGetCurrentDataConnectionList =
mPhone.getClass().getMethod("getCurrentDataConnectionList", null);
List<Object> dcs = (List)mGetCurrentDataConnectionList.invoke(mPhone, null);
Method mGetConnectionTime =
dcs.get(0).getClass().getMethod("getConnectionTime", null);
long getConnectionTime = (Long)mGetConnectionTime.invoke(dcs.get(0), null);
} catch(Exception e) {
e.printStackTrace();
}
The List "dcs" contains the GSMDataConnection that should be there, but the information is not correct, it always says GSM Connections are inactive, that's not true.
Any Idea?
Thanks!
Use the Java clock interface with the Connectivity Manager, and subtract the difference between start and end times of the connection.
I am creating a webview based Android Application using Phonegap. To help the application, I have created a service that basically gets user's location from time to time and processes it and saves it.
This is what happens:
I run the application - I have startService() call in onCreate() of the MainActivity. There is no other activity in the application (until now).
The service runs, application runs. I can see all this in LogCat.
Now, when I press back key on application's first screen, application exits and as a result after few seconds I see stack trace in LogCat and message that application has stopped. The error is NullPointerException
I get the exception in method below at indicated line:
public void GetAvailableLocation(){
vstore = new VariableStorage(); //Even when I assigned new object to vstore
if(vstore.load("mobileNumber").equals("0")) // Exception occures here
return;
// Get all available providers
List<String> providers = locationManager.getAllProviders();
for(String provider: providers) {
Location newLocation = locationManager.getLastKnownLocation(provider);
if(isBetter(newLocation, locationListener.location)
&& newLocation != null) {
locationListener.location = newLocation;
}
}
}
The above method is first method called in onCreate() of service.
Please help me out on this.
Edit: here is the load method in vstore-
public String load(String key){
Log.d(TAG, "Load key: "+key);
try{
if(!loaded){
this.loadFromFile();
}
String result = null;
if(key.equals("loggedIn"))
result = Boolean.toString(loggedIn);
else if(key.equals("mobileNumber"))
result = Long.toString(mobileNumber);
else if(key.equals("password"))
result = password;
else if(key.equals("gettingService"))
result = Boolean.toString(gettingService);
else if(key.equals("providingService"))
result = Boolean.toString(providingService);
else if(key.equals("gettingServiceID"))
result = Integer.toString(gettingServiceID);
else if(key.equals("providingServiceTo"))
result = Long.toString(providingServiceTo);
else if(key.equals("usersName"))
result = usersName;
else if(key.equals("currLatitude"))
result = Double.toString(currLatitude);
else if(key.equals("currLongitude"))
result = Double.toString(currLongitude);
else if(key.equals("prevLatitude"))
result = Double.toString(prevLatitude);
else if(key.equals("prevLongitude"))
result = Double.toString(prevLongitude);
else if(key.equals("lastLocationUpdateTime"))
result = Integer.toString(lastLocationUpdateTime);
else if(key.equals("publicKey"))
result = publicKey;
else if(key.equals("notification"))
result = Integer.toString(notification);
else if(key.equals("verifyMobileNumber"))
result = Long.toString(verifyMobileNumber);
return result;
}
catch(Exception e){
Log.d(TAG, "VSLoad Error: " + e.getMessage());
return null;
}
}
that is a better way to write that condition:
if("0".equals(vstore.load("mobileNumber")))
"0" is always given. so if load returns null you will call return;
That is called null saved :)
Be sure that vstore.load("mobileNumber") returns something
or write something like:
if(vstore.load("mobileNumber") == null || vstore.load("mobileNumber").equals("0"))
return;