I am writing an app that plays 4 mp3 files simultaneously using 4 MediaPlayer objects. I have added 3 custom knobs to control bass, mid, and treble using 4 equalizers, each attached to its specific MediaPlayer. I have verified that I am passing the correct values (in Millibels) to setBandLevel for each as specified, but I don't hear any difference in sound when spinning the controls. I have added the relevant code below.
One thing I noticed that seems really odd is that the Equalizer method I am calling expects a short, but the frequency range upper limits come back as high as 6,000,000 millibels. I may be missing something here, but a short in android is 2^16 which should mean an upper limit of 64k.
public void setBandLevel(short band, short level)
The code runs without hitting exceptions and I have traces all values to verify each frequency is within its band's range, but I get no variance in sound when I spin the control knobs. I have tested this on K, L and M with the same results.
I appreciate any insight into this.
public void onResume() {
super.onResume();
// Load an ad into the AdMob banner view.
AdView adView = (AdView) getView().findViewById(R.id.adView);
AdRequest adRequest = new AdRequest.Builder()
.setRequestAgent("android_studio:ad_template").build();
adView.loadAd(adRequest);
//initialize views
initializeViews();
setInstrumentNames();
mediaPlayer1.start();
mediaPlayer2.start();
mediaPlayer3.start();
mediaPlayer4.start();
// Start the songtime thread and remember its object so it can be stopped legally
play();
setEqualizer();
setupKnobButtons();
}
public void setEqualizer() {
eq1 = new Equalizer(0, mediaPlayer1.getAudioSessionId());
eq2 = new Equalizer(0, mediaPlayer2.getAudioSessionId());
eq3 = new Equalizer(0, mediaPlayer3.getAudioSessionId());
eq4 = new Equalizer(0, mediaPlayer4.getAudioSessionId());
if (eq1 != null && eq2 != null && eq3 != null && eq4 != null) {
int z = eq1.setEnabled(true);
int c = eq2.setEnabled(true);
int d = eq3.setEnabled(true);
int e = eq4.setEnabled(true);
// The number of bands is usually 5 but could be up to 13
num_bands = eq1.getNumberOfBands();
// The band range will always return the min and max
// Gets the level range for use by setBandLevel(short,short)}. The level is expressed in
// milliBel. Return the band level range in an array of short integers. The first element is the lower
// limit of the range, the second element the upper limit.
// --- THIS IS FOR THE FULL RANGE OF ALL BANDS ---///
range = eq1.getBandLevelRange();
min_level = range[0];
max_level = range[1];
Log.d(TAG_BANDLEVELS, "min level = " + min_level + " max level = " + max_level);
// Loop through all the bands ( usually 5 ) and create 2 arrays
// ( one for lower limit of each band and one for the upper )
for (int i = 0; i < num_bands; i++) {
freq_range = eq1.getBandFreqRange((short) i);
freqRangeLowerLimit.add(freq_range[0]);
freqRangeUpperLimit.add(freq_range[1]);
Log.d(TAG_BANDLEVELS, "freqRangeLowerLimit = " + freq_range[0] +
" freqRangeUpperLimit = " + freq_range[1] + " band = " + i);
}
}
}
private void setBandLevels(int percentage, int new_level1, int new_level2, int nBand) {
if (eq1.hasControl()) {
eq1.setBandLevel((short) nBand, (short) new_level1);
}
else
Log.d(TAG_BANDLEVELS, "EQ 1 DOES NOT HAVE CONTROL");
if (eq2.hasControl()) {
eq2.setBandLevel((short) nBand, (short) new_level1);
}
else
Log.d(TAG_BANDLEVELS, "EQ 2 DOES NOT HAVE CONTROL");
if (eq3.hasControl()) {
eq3.setBandLevel((short) nBand, (short) new_level1);
}
else
Log.d(TAG_BANDLEVELS, "EQ 3 DOES NOT HAVE CONTROL");
if (eq4.hasControl()) {
eq4.setBandLevel((short) nBand, (short) new_level1);
}
else
Log.d(TAG_BANDLEVELS, "EQ 4 DOES NOT HAVE CONTROL");
// Mid and Treble use 2 bands (Mid = 2nd and 3rd Treble = 4th and 5th
if (nBand == PlayFragment.BAND_BASS || nBand == PlayFragment.BAND_TREBLE) {
try {
eq1.setBandLevel((short) (nBand+1), (short) new_level2);
eq2.setBandLevel((short) (nBand+1), (short) new_level2);
eq3.setBandLevel((short) (nBand+1), (short) new_level2);
eq4.setBandLevel((short) (nBand+1), (short) new_level2);
} catch (Throwable ex2) {
ex2.printStackTrace();
}
}
}
public void setupKnobButtons() {
RoundKnobButton rnbBass = (RoundKnobButton) getView().findViewById(R.id.btRoundBass);
rnbBass.SetListener(new RoundKnobButtonListener() {
TextView tvEQBass = (TextView) getView().findViewById(R.id.tvEQBass);
public void onStateChange(boolean newstate) {
Toast.makeText(getActivity(), "New state:" + newstate, Toast.LENGTH_SHORT).show();
}
public void onRotate(final int percentage) {
tvEQBass.post(new Runnable() {
public void run() {
tvEQBass.setText("\n" + percentage + "%\n");
// Bass only uses the first band
int lower1 = freqRangeLowerLimit.get(0);
int upper1 = freqRangeUpperLimit.get(0);
int new_level1 = lower1 + ((upper1 - lower1) * percentage/100);
// new_level1 /= 1000;
// int new_level1 = calc1/100;
int lower2 = freqRangeLowerLimit.get(1);
int upper2 = freqRangeUpperLimit.get(1);
int new_level2 = lower1 + ((upper2 - lower2) * percentage/100);
// new_level2 /= 1000;
// int new_level2 = calc2/100;
setBandLevels(percentage, new_level1, new_level2, BAND_BASS);
Log.d(TAG_BANDLEVELS, "Band = Bass " + "New Level1 = " + new_level1 + "New Level2 = " + new_level2 + " Percentage = " + percentage);
}
});
}
});
Related
Hello all for the second time,
Initially I was looking for a broad answer, but this thread got blocked for being "too broad"... so I've got no choice, but to go into detail. My apologies if asking the question again is against the forum guidelines, I'm new to stackoverflow so please be kind.
I’ve got data coming into a serial port at 250Hz and I’d like to save it all to a .csv file. Of course draw() is not going to be able to keep up with that rate of data...
At the moment I am using the serialEvent(port) to collect and parse the data. Once parsed out, I'm calling a function in draw to add the data to a new line in a table and then saving that table every 5 seconds...
Yes, I see the obvious flaw that if I'm saving the current data in draw then of course it's not going to be able to save all the data coming in, but rather just the data that happens to be present when the data saving function is called... but I'm not sure of the best way to solve that. A buffer scheme? Or can I have a separate thread that just adds ALL data coming in to a table?
which lead to my initial (broad) question...
Is there a way to save all incoming data to a file without polling?
Thanks to all in advance.. code below:
Twain
import processing.serial.*;
import static javax.swing.JOptionPane.*;
Table table;
String Path = "PathProvidedHere.csv";
String message;
//Some time keeping variables
int hours, minutes, seconds, milliseconds;
float SaveTime;
//Serial port selection
Serial myPort;
String COMx, COMlist = "";
final boolean debug = true;
String portName;
// Data variables
float yaw = 0.0; float pitch = 0.0; float roll = 0.0;
float A1, A2, A3, A4;
float E1, E2, E3, E4;
void setup()
{
//Set up GIU box
size(1024, 768, P3D);
frameRate(250);
smooth();
//Some other setups like fonts, graphs, etc.
//Set up the logging table
table = new Table();
table.addColumn("A1"); table.addColumn("A2"); table.addColumn("A3"); table.addColumn("A4");
table.addColumn(""); table.addColumn("E1"); table.addColumn("E3"); table.addColumn("E4");
table.addColumn(" "); table.addColumn("min"); table.addColumn("sec"); table.addColumn("milli");
portName = chooseCOM();
delay(1000);
}
void draw()
{
SavetoCSV();
//serialEvent(myPort); // read and parse incoming serial message
ACouple();
Unrelated();
FunctionsHere();
if(millis() - SaveTime > 5000)
{
saveTable(table, Path);
SaveTime=millis();
}
}
String chooseCOM()
{
setupP2 = true;
try
{
if (debug) printArray(Serial.list());
int i = Serial.list().length;
if (i != 0)
{
if (i >= 2)
{
// need to check which port the inst uses -
// for now we'll just let the user decide
for (int j = 0; j < i; )
{
COMlist += char(j+'a') + " = " + Serial.list()[j];
if (++j < i) COMlist += ", ";
}
COMx = showInputDialog("Which COM port is correct? (a,b,..):\n"+COMlist);
if (COMx == null) exit();
if (COMx.isEmpty()) exit();
i = int(COMx.toLowerCase().charAt(0) - 'a') + 1;
}
String portName = Serial.list()[i-1];
if (debug) //println(portName + " Selected");
myPort = new Serial(this, portName, 115200); // change baud rate to your liking
myPort.bufferUntil(13); // buffer until CR/LF appears, but not required..
return portName;
}
else
{
showMessageDialog(frame, "Device is not connected to the PC");
exit();
}
}
catch (Exception e)
{ //Print the type of error
showMessageDialog(frame, "COM port is not available (may\nbe in use by another program)");
//println("Error:", e);
exit();
}
return "noPort";
}
void serialEvent(Serial myPort)
{
int newLine = 13; // new line character in ASCII
do
{
message = myPort.readStringUntil(newLine); // read from port until new line
if (message != null)
{
String[] list = split(trim(message), " ");
if (list.length == 4 && list[0].equals("i"))
{
yaw = float(list[1]); // convert to float yaw
pitch = float(list[2]); // convert to float pitch
roll = float(list[3]); // convert to float roll
}
else if (list.length == 5 && list[0].equals("s"))
{
A1 = float(list[1]);
A2 = float(list[2]);
A3 = float(list[3]);
A4 = float(list[4]);
}
else if (list.length >=2 && list[0].equals("b"))
{
Battery = int(list[1]);
}
else if (list.length >= 2 && list[0].equals("m"))
{
MACid = int(list[1]);
}
else
{
//print anything extra to console
//println(message);
}
}
} while (message != null);
}
void SavetoCSV()
{
if (A1 != 0)
{
TableRow newRow = table.addRow();
newRow.setFloat("A1", (A1));
newRow.setFloat("A2", (A2));
newRow.setFloat("A3", (A3));
newRow.setFloat("A4", (A4));
//saveTable(table, Path);
}
}
Additional info:
- Processing P3
- For the record, with the rest of my script I can get draw up to 80hz or so
- I'd be okay with saving all the data and parsing it later
Went the buffer route.... I think I'm getting close now. Unsure if I'm saving the data in the right order or if the saving process will halt the rest of the processes...
Code:
import processing.serial.*;
import static javax.swing.JOptionPane.*;
//Arrays to save the data
LinkedList<Integer> A1c = new LinkedList<Integer>();
LinkedList<Integer> A2c = new LinkedList<Integer>();
LinkedList<Integer> A3c = new LinkedList<Integer>();
LinkedList<Integer> A4c = new LinkedList<Integer>();
int bufferLength = 500;
int bufflen = 0;
//Serial port selection
Serial myPort;
String COMx, COMlist = "";
final boolean debug = true;
String portName;
// Data variables
float yaw = 0.0; float pitch = 0.0; float roll = 0.0;
float A1, A2, A3, A4;
//Data log variables
Table table;
String Path = "PathtoFile.csv";
void setup() {
//Set up GIU box
size(1024, 768, P3D);
frameRate(250);
strokeWeight(50);
smooth();
//Set up the logging table
table = new Table();
table.addColumn("A1"); table.addColumn("A2"); table.addColumn("A3"); table.addColumn("A4");
portName = chooseCOM();
}
void draw() {
//SavetoCSV now called within SerialEvent()
//SavetoCSV();
//serialEvent(myPort); // read and parse incoming serial message
Some();
Unrelated();
FunctionsHere();
}
void serialEvent(Serial myPort) {
int newLine = 13; // new line character in ASCII
do {
message = myPort.readStringUntil(newLine); // read from port until new line
if (message != null) {
String[] list = split(trim(message), " ");
if (list.length == 4 && list[0].equals("i")) {
yaw = float(list[1]); // convert to float yaw
pitch = float(list[2]); // convert to float pitch
roll = float(list[3]); // convert to float roll
} else if (list.length == 5 && list[0].equals("s")) {
A1 = float(list[1]);
A2 = float(list[2]);
A3 = float(list[3]);
A4 = float(list[4]);
if (bufflen < bufferLength) {
A1c.push(int(A1));
A2c.push(int(A2));
A3c.push(int(A3));
A4c.push(int(A4));
bufflen++;
}
else{
bufflen = 0;
SavetoCSV();
}
} else if (list.length >=2 && list[0].equals("b")) {
Battery = int(list[1]);
} else if (list.length >= 2 && list[0].equals("m")) {
MACid = int(list[1]);
} else {
//print anything extra to console
//println(message);
}
}
} while (message != null);
}
void SavetoCSV() {
if (A1 != 0) {
for (int i = bufferLength - 1; i >= 0; i--){
if (i < bufferLength){
TableRow newRow = table.addRow();
newRow.setFloat("A1", (A1c.get(i)));
newRow.setFloat("A2", (A2c.get(i)));
newRow.setFloat("A3", (A3c.get(i)));
newRow.setFloat("A4", (A4c.get(i)));
} else saveTable(table, Path);
}
}
}
String chooseCOM() {
setupP2 = true;
try {
if (debug) printArray(Serial.list());
int i = Serial.list().length;
if (i != 0) {
if (i >= 2) {
// need to check which port the inst uses -
// for now we'll just let the user decide
for (int j = 0; j < i; ) {
COMlist += char(j+'a') + " = " + Serial.list()[j];
if (++j < i) COMlist += ", ";
}
COMx = showInputDialog("Which COM port is correct? (a,b,..):\n"+COMlist);
if (COMx == null) exit();
if (COMx.isEmpty()) exit();
i = int(COMx.toLowerCase().charAt(0) - 'a') + 1;
}
String portName = Serial.list()[i-1];
if (debug) //println(portName + " Selected");
myPort = new Serial(this, portName, 115200); // change baud rate to your liking
myPort.bufferUntil(13); // buffer until CR/LF appears, but not required..
return portName;
} else {
showMessageDialog(frame, "Device is not connected to the PC");
exit();
}
}
catch (Exception e)
{ //Print the type of error
showMessageDialog(frame, "COM port is not available (may\nbe in use by another program)");
//println("Error:", e);
exit();
}
return "noPort";
}
I'm trying to make the second sound on this app. oscillate in volume based on a Sin curve as a function of time passing. How would you suggest I edit what I already have to make that work? Any help would be amazing!
I'm sorry If some of the code is bad, I stepped away from this for some time and I added some sections back in that I previously commented out. I wasn't sure if it was for a good reason or not, so I added it back in.
MediaPlayer beep;
MediaPlayer sound2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); //call superclass onCreate
setContentView(R.layout.activity_main); //inflate the GUI
minutesChanged = (TextView) findViewById(R.id.minutesChanged);
secondsChanged = (TextView) findViewById(R.id.secondsChanged);
minutesTotal = (TextView) findViewById(R.id.totalMinutesEntered);
secondsTotal = (TextView) findViewById(R.id.totalSecondsEntered);
percentageFinishedAmount = (TextView) findViewById(R.id.percentageFinishedAmount);
errorMessage = (TextView) findViewById(R.id.errorMessage);
secondEditText =
(EditText) findViewById(R.id.secondEditText);
secondEditText.addTextChangedListener(secondEditTextWatcher);
minuteEditText =
(EditText) findViewById(R.id.minuteEditText);
minuteEditText.addTextChangedListener(minuteEditTextWatcher);
whichSound =
(EditText) findViewById(R.id.whichSound);
whichSound.addTextChangedListener(whichSoundTextWatcher);
seekBarSeconds =
(SeekBar) findViewById(R.id.seekBarSeconds);
seekBarSeconds.setOnSeekBarChangeListener(seekBarListener);
seekBarMinutes =
(SeekBar) findViewById(R.id.seekBarMinutes);
seekBarMinutes.setOnSeekBarChangeListener(seekBarListener);
percentSeekBar =
(SeekBar) findViewById(R.id.seekBarTotal);
beep = MediaPlayer.create(MainActivity.this, R.raw.beep);
sound2 = MediaPlayer.create(MainActivity.this, R.raw.sound);
}
private void calculate() {
totalmili = minutesT + secondsT;
new CountDownTimer(totalmili + 2000, interv) {
public void onTick(long millisUntilFinished) {
secondsEllapsed = (int) ((totalmili + 2000 - millisUntilFinished) / 1000);
minutesEllapsed = secondsEllapsed / 60;
secondsEllapsed = secondsEllapsed % 60;
totalTimeEllapsed = secondsEllapsed + (minutesEllapsed * 60);
Log.d("Lucas", "total time elapsed " + totalTimeEllapsed);
if (totalTimeEllapsed < 10) {
errorMessage.setText("Elapsed Time: " + minutesEllapsed + " : 0" + secondsEllapsed);
} else if (totalTimeEllapsed >= 10) {
errorMessage.setText("Elapsed Time: " + minutesEllapsed + " : " + secondsEllapsed);
}
//set seekBar minutes/seconds and changing minute/seconds textView
seekBarMinutes.setProgress(minutesEllapsed);
minutesChanged.setText(Integer.toString(minutesEllapsed));
seekBarSeconds.setProgress(secondsEllapsed);
secondsChanged.setText(Integer.toString(secondsEllapsed));
double t = sin((360/totalmili) *(totalTimeEllapsed*1000));
float left = (float) ((t+1.0)*0.9/2.0+0.1);
float right = (float) ((t+1.0)*0.9/2.0+0.1);
sound2.setVolume(left,right);
Log.d("Lucas", "value of left and red respectively " + left + ", " +right);
if(sound == 1){
musicPlaying = beep.isPlaying();
if(musicPlaying == true){
beep.pause();
beep.seekTo(0);
beep.start();
} else {
beep.start();
}
}else if (sound == 2){
if(start ==1) {
sound2.seekTo(19);
sound2.start();
start = 2;
float left = (float) ((sin(totalTimeEllapsed)+1.0)*0.9/2.0+0.1);
float right = (float) ((sin(totalTimeEllapsed)+1.0)*0.9/2.0+0.1);
sound2.setVolume(left, right);
Log.d("Lucas", "value of left and red respectively " + left + ", " +right);
musicPlaying = sound2.isPlaying();
}else if(start ==2){
if (totalTimeEllapsed == 11) {
sound2.pause();
sound2.seekTo(34);
sound2.start();
} else if (totalTimeEllapsed == 22) {
sound2.pause();
}
}
//calculate total percentage finished, set percentage text, set total percentage seekBar
int percentProgress = (int) Math.round(((double) totalTimeEllapsed) / ((double) (totalmili) / 1000) * 100);
percentageFinishedAmount.setText(percentProgress + "% ");
percentSeekBar.setProgress(percentProgress);
//change background and text color bassed on increasing progress
errorMessage.setBackgroundColor(Color.argb(255, (int) (percentProgress * 2.5), 0, 0));
errorMessage.setTextColor(Color.argb(255, (int) (255 - (percentProgress * 2.5)), 255, 255));
}
public void onFinish() {
//display on finish text
errorMessage.setText("Done!");
}
}.start();
}
//listener object for the EditText's text-changed events
private final TextWatcher minuteEditTextWatcher = new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start,
int before, int count) {
// set hours Total and converts minutes to milliseconds
if (s.charAt(start + count - 1) == '\n') {
minuteEditText.getText().replace(start + count - 1, start + count, " ");
s = minuteEditText.getText();
Log.d("Lucas", "in enter key min, s = " + s);
try {
minutesT = Integer.parseInt(s.toString());
minutesTotal.setText(String.valueOf(minutesT) + " Minutes");
percentageFinishedAmount.setText(" ");
minutesT *= 60000;
} catch (NumberFormatException e) {
minutesTotal.setText("");
minutesT = 0;
}
}
}
#Override
public void afterTextChanged(Editable s) {
}
#Override
public void beforeTextChanged(
CharSequence s, int start, int count, int after) {
}
};
// listener object for the EditText's text-changed events
private final TextWatcher secondEditTextWatcher = new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start,
int before, int count) {
// set minutes Total when text is changed, calls calculate, changes seconds to milliseconds
if (s.charAt(start + count - 1) == '\n') {
secondEditText.getText().replace(start + count - 1, start + count, " ");
s = secondEditText.getText();
Log.d("Lucas", "in enter key sec, s = " + s);
try {
secondsT = Integer.parseInt(s.toString());
secondsTotal.setText(String.valueOf(secondsT) + " Seconds");
secondsT *= 1000;
percentageFinishedAmount.setText(" ");
calculate();
} catch (NumberFormatException e) {
secondsTotal.setText("");
secondsT = 0;
}
}
}
#Override
public void afterTextChanged(Editable s) {
}
#Override
public void beforeTextChanged(
CharSequence s, int start, int count, int after) {
}
};
Yowza. I'm not really clear on what the requirements are here or what you are doing.
My approach would be radically different, and maybe missing the point of what you are attempting. It would have these elements:
1) Output via a SourceDataLine, as this gives you a handle to affect the volume of every frame.
2) Per frame, consult a pointer into a Sin LUT that has the same number of elements as needed to correspond to the desired rate. (But it could also be fine to just create an argument into a sine function that increments the correct amount each frame to get your desired rate. No need to get into a war about whether the function or the LUT performs better.)
3) Multiply the SDL frame by the Sin LUT value.
For example an LFO of 1 Hz would imply either an LUT of 44100 elements, or an argument that increments by 1/44100 per frame.
As for getting access to the individual sound frames, see the tutorial Using Files and Format Converters/Reading Sound Files and look for the point in the example with the comment "Here, do something useful with the audio data that's now in the audioBytes array". That useful thing would be to convert the bytes to PCM (there are other StackOverflow explain how), get your next sin function value and multiply, then convert back to bytes.
EDIT:
Looking into the MediaPlayer API, I'm not clear you will be able to efficiently do what you want with this class.
A player is not prepared to respond to commands quasi-immediately
until its status has transitioned to MediaPlayer.Status.READY, which
in effect generally occurs when media pre-roll completes.
Does this imply some sort of buffer-size that will limit the frequency at which you can make the volume updates? IDK. When we get into buffer-size issues that in effect enforce a maximum number of updates per second, one has to listen carefully to check that there are no clicks or zippering due to discontinuities when the volume jumps from one level to another. A lot also depends on the amount of overhead happening under the hood when you set the position for each volume change.
With a SourceDataLine output, you have clean access to each frame which pretty much eliminates the potential zippering problems. I should add disclaimer though that I haven't grappled with MediaPlayer and may be ignorant of capabilities that you can take advantage of. Regardless, SDL is low level, powerful and efficient.
I have to implement Go To Page feature in epub reader. I have try to implement this feature in source code of Page-Turner, but its not working successfully because of multiple xhtml in .epub file, as we know that each chapter have single xhtml file and its divide as per screen size in this app. So whenever screen size is big then total number of pages are less and more number of pages when screen is small, So there is no fix page number where to jump. I have edit and try to implement like giver below.
ReadingFragment.java
public void performSearch(String query) {
int index = Integer.parseInt(query);
if (index > bookView.getTotalNumberOfPages()) {
Toast.makeText(context, "Please enter number between 0 to " + bookView.getTotalNumberOfPages(), Toast.LENGTH_SHORT).show();
} else {
bookView.gotoPageNumber(index);
}
}
BookView.java
public void gotoPageNumber(int pageNum) {
strategy.gotoPage(pageNum);
progressUpdate();
}
PageChangeStrategy.java
public void gotoPage(int pageNumber);
FixedPagesStrategy.java
#Override
public void gotoPage(int pageNumber) {
PageTurnerSpine spinePos = bookView.getSpine();
this.storedPosition = -1;
int currentPage = getCurrentPage() + spinePos.getUptoPage(spinePos.getPosition());
Log.e(TAG, "Adding >> Upto Page : " + spinePos.getUptoPage(spinePos.getPosition())
+ ", currentPage : " + getCurrentPage());
Log.e(TAG, "pagenum : " + pageNum);
if (pageNumber > currentPage) { //pageNumber is greater then current page
int jumpSpine = spinePos.getIndexFromPage(pageNumber);
int currentSpine = spinePos.getPosition();
Log.e(TAG, "jumpSpine : " + jumpSpine + ", currentSpine : " + currentSpine);
if (jumpSpine == currentSpine) {
int diffrence = pageNumber - currentPage;
Log.e(TAG, "diffrence < : " + diffrence);
Log.e(TAG, "Minimum >> PageOffSets - 1 : " + (spinePos.getPageOffSets(currentSpine) - 1)
+ ", pageNum + diffrence : " + (pageNum + diffrence));
this.pageNum = Math.min(pageNum + diffrence, spinePos.getPageOffSets(currentSpine) - 1);
updatePosition();
} else {
PageTurnerSpine spine = bookView.getSpine();
if (spine == null || !spine.navigateFrontSpine(spine.getIndexFromPage(pageNumber))) {
return;
}
this.pageNum = 0;
gotoPage(pageNumber);
}
} else { //pageNumber is less then current page
int jumpSpine = spinePos.getIndexFromPage(pageNumber);
int currentSpine = spinePos.getPosition();
Log.e(TAG, "jumpSpine : " + jumpSpine + ", currentSpine : " + currentSpine);
if (jumpSpine == currentSpine) {
int diffrence = currentPage - pageNumber;
Log.e(TAG, "diffrence > : " + diffrence);
Log.e(TAG, "pagenum - diffrence : " + (pageNum - diffrence));
this.pageNum = Math.max(pageNum - diffrence, 0);
updatePosition();
} else {
PageTurnerSpine spine = bookView.getSpine();
if (spine == null || !spine.navigateBackSpine(spine.getIndexFromPage(pageNumber))) {
return;
}
this.pageNum = 0;
gotoPage(pageNumber);
}
}
Log.e(TAG, "In last pageNum : " + pageNum);
}
PageTurnerSpine.java
public int getIndexFromPage(int pageNumber) {
int total = 0;
int totalIndex = 0;
for (List<Integer> pagesPerSection : pageOffsets) {
total += pagesPerSection.size();
totalIndex = totalIndex + 1;
if (total - 1 >= pageNumber) {
return totalIndex - 1;
}
}
return 0;
}
public int getUptoPage(int position) {
int total = 0, max = 0;
for (List<Integer> pagesPerSection : pageOffsets) {
max = max + 1;
if (position == max - 1) {
return total;
}
total += pagesPerSection.size();
}
return 0;
}
public int getPageOffSets(int position) {
int max = 0;
for (List<Integer> pagesPerSection : pageOffsets) {
max = max + 1;
if (position == max - 1) {
return pagesPerSection.size();
}
}
return 0;
}
public boolean navigateFrontSpine(int indexSpine) {
if (this.position == size() - 1) {
return false;
}
this.position = indexSpine;
return true;
}
public boolean navigateBackSpine(int indexSpine) {
if (this.position == 0) {
return false;
}
this.position = indexSpine;
return true;
}
When i apply this code and run some time it work correctly. but some time it jump on other page number, like if i enter 110 then it will jump on 109. and when i am trying to jump on chapter starting page then contain dose not changes. Please help me.
https://github.com/NightWhistler/PageTurner
In this source code i have edited some file given above. they already exists in this project.
Mostly i see that most of epub reader like Kindle, FBReader, etc... does not implement Go To Page feature. So, i want to also know that is it possible to implement this feature or not?
Thanks for Help :)
You really need to consult the IDPF Epub standards at IDPF.org/epub .
One approach, that I think is optional within the standard (but am not sure about) is to mark the content with the physical page numbers from the paper book (if there is one), or decide on your own numbering system along with your Table of Contents, and use a corresponding virtual page as the start of it.
This enables going to the start of the same page, but the number of virtual pages per physical page will vary, depending on font sizes etc. currently in use.
It's a data issue as much as a programming one.
I'm working on a minigames plugin. After an arena finishes it should be regenerated - I use the unload and load trick. It has an obvious disadvantage - it freezes the server for a while to prepare spawn areas. I decided to put the arena reset code into an runnable asynchronous task runTaskAsynchronously(). However, when the server tries to run the code inside the thread, it throws an exception:
Caused by: java.lang.IllegalStateException: Asynchronous entity world add!
Here's a part of my code:
getServer().getScheduler().runTaskAsynchronously(this, new Runnable()
{
#Override
public void run()
{
String w_name = world.getName();
getServer().unloadWorld(world.getName(), false);
world = getServer().createWorld(new WorldCreator(w_name));
}
});
Any suggestions how to deal with this problem?
Bukkit doesn't like it when you try to edit anything through the API in an async task. Reading and processing is fine but bukkit enforces nothing when it comes to thread safety and thus affecting the world with more than 1 thread can cause issues.
Try splitting up your arena reset into smaller chunks and spreading out the operation over several ticks with a series of synchronous tasks, might help with the performance.
This isn't my code but it does a decent job of demonstrating the idea https://gist.github.com/aadnk/5443172
-- You can load worlds async using this: http://pastebin.com/K9CuVMS5 --
No you cannot, and if you tried it would have a high chance of world corruption. But if you don't care about it this is what you can do:
Bukkit loads worlds via Bukkit.createWorld(WorldCreator) which activates Server.createWorld(WorldCreator) which activates:
Validate.notNull(creator, "Creator may not be null");
String name = creator.name();
ChunkGenerator generator = creator.generator();
File folder = new File(this.getWorldContainer(), name);
World world = this.getWorld(name);
WorldType type = WorldType.getType(creator.type().getName());
boolean generateStructures = creator.generateStructures();
if(world != null) {
return world;
} else if(folder.exists() && !folder.isDirectory()) {
throw new IllegalArgumentException("File exists with the name \'" + name + "\' and isn\'t a folder");
} else {
if(generator == null) {
generator = this.getGenerator(name);
}
WorldLoaderServer converter = new WorldLoaderServer(this.getWorldContainer());
if(converter.isConvertable(name)) {
this.getLogger().info("Converting world \'" + name + "\'");
converter.convert(name, new ConvertProgressUpdater(this.console));
}
int dimension = 10 + this.console.worlds.size();
boolean used = false;
do {
Iterator sdm = this.console.worlds.iterator();
while(sdm.hasNext()) {
WorldServer hardcore = (WorldServer)sdm.next();
used = hardcore.dimension == dimension;
if(used) {
++dimension;
break;
}
}
} while(used);
boolean var25 = false;
ServerNBTManager var24 = new ServerNBTManager(this.getWorldContainer(), name, true);
WorldData worlddata = var24.getWorldData();
if(worlddata == null) {
WorldSettings internal = new WorldSettings(creator.seed(), EnumGamemode.getById(this.getDefaultGameMode().getValue()), generateStructures, var25, type);
internal.setGeneratorSettings(creator.generatorSettings());
worlddata = new WorldData(internal, name);
}
worlddata.checkName(name);
WorldServer var26 = (WorldServer)(new WorldServer(this.console, var24, worlddata, dimension, this.console.methodProfiler, creator.environment(), generator)).b();
if(!this.worlds.containsKey(name.toLowerCase())) {
return null;
} else {
var26.scoreboard = this.getScoreboardManager().getMainScoreboard().getHandle();
var26.tracker = new EntityTracker(var26);
var26.addIWorldAccess(new WorldManager(this.console, var26));
var26.worldData.setDifficulty(EnumDifficulty.EASY);
var26.setSpawnFlags(true, true);
this.console.worlds.add(var26);
if(generator != null) {
var26.getWorld().getPopulators().addAll(generator.getDefaultPopulators(var26.getWorld()));
}
this.pluginManager.callEvent(new WorldInitEvent(var26.getWorld()));
System.out.print("Preparing start region for level " + (this.console.worlds.size() - 1) + " (Seed: " + var26.getSeed() + ")");
if(var26.getWorld().getKeepSpawnInMemory()) {
short short1 = 196;
long i = System.currentTimeMillis();
for(int j = -short1; j <= short1; j += 16) {
for(int k = -short1; k <= short1; k += 16) {
long l = System.currentTimeMillis();
if(l < i) {
i = l;
}
if(l > i + 1000L) {
int chunkcoordinates = (short1 * 2 + 1) * (short1 * 2 + 1);
int j1 = (j + short1) * (short1 * 2 + 1) + k + 1;
System.out.println("Preparing spawn area for " + name + ", " + j1 * 100 / chunkcoordinates + "%");
i = l;
}
BlockPosition var27 = var26.getSpawn();
var26.chunkProviderServer.getChunkAt(var27.getX() + j >> 4, var27.getZ() + k >> 4);
}
}
}
this.pluginManager.callEvent(new WorldLoadEvent(var26.getWorld()));
return var26.getWorld();
}
}
Now by creating you own world loader, you can make it so it only generates a chunk every tick or so.
i'm posting a simple game code, written in java. It took my hours today to write this but now i'm stuck! The problem is, in the second level of this game (after failure or success) i can't see the red tiles. It works only the first time.
games logic:
it starts up with a 3x3 matrix and rebound the dimension of this matrix in case of success (testing the ability of memory, memorising the coordinates of red tiles in 1200 ms). So we show the red tiles first, than we check the estimations. If there became a wrong try, failure!. If it is a bigger matrix than 3x3, it gets smaller.
the code is a little bit long but just one class, so it is so easy yo execute it. If you have time i would be appreciate.
So there it goes:
package skeleton;
public class Memory extends JFrame implements ActionListener {
private static final long serialVersionUID = 5963518754230629235L;
private static int minDimX = 3, minDimY = 3;
private static int maxDimX = 15, maxDimY = 15;
private static int counter = 13;
private static int memoriseTime = 1200; // milliseconds
private static int memGridWidthX = 60;
private static int memGridWidthY = 60;
private JPanel centerPanel;
private JButton memTile;
private Random generator;
private int memGridDimX, memGridDimY, coef, numberOfMemTilesToGuess;
int[][] memTileCoordinates;
int[] randomNums;
Border grayBorder = LineBorder.createGrayLineBorder();
public Memory(int xDim, int yDim) {
waitALittleBit(1300);
memGridDimX = xDim;
memGridDimY = yDim;
coef = 3;
numberOfMemTilesToGuess = memGridDimX*memGridDimY / coef;
centerPanel = new JPanel();
centerPanel.setLayout(new GridLayout(memGridDimY, memGridDimX, 0, 0));
add(centerPanel, BorderLayout.CENTER);
System.out.println("int[" + memGridDimX + "][" + memGridDimY + "] array is created ");
randomNums = new int[numberOfMemTilesToGuess];
for (int i = 0 ; i < numberOfMemTilesToGuess ; i ++) {
System.out.println("> we are in for the "+ (i+1) +". time!");
int randomNum;
boolean randomNumberAlreadySelected = false;
do {
randomNum = calculateARandomNumber(memGridDimX * memGridDimY);
if (i != 0) { //for the first time, we don't need to compare any numbers
randomNumberAlreadySelected = isThisRandomNumberExistInAlreadyFoundRandomNumbersArray(randomNum, randomNums, i);
if (randomNumberAlreadySelected)
System.out.println("######## RECALCULATING RANDOM NUMBER !! ##########");
else
System.out.println("They are not equal, go on!");
}
} while (randomNumberAlreadySelected);
randomNums[i] = (Integer)randomNum;
}
//show the memory tiles
setMemTiles(randomNums, true);
waitALittleBit(memoriseTime);
//hide the memory tiles
setMemTiles(randomNums, false);
}
private int calculateARandomNumber(int limit) {
generator = new Random();
System.out.println("* Calculating random number which is smaller than " + memGridDimX * memGridDimY);
int randomNum = generator.nextInt() % (memGridDimX * memGridDimY);
System.out.println("- Calculated random number: " + randomNum);
if (randomNum < 0) {
System.out.println(".. it is negative, so we add: " + memGridDimX * memGridDimY);
randomNum += memGridDimX * memGridDimY;
}
System.out.println(".. and we add 1 to have a grid array between 1 and 12");
randomNum += 1;
System.out.println(".. and our new random number is: " + randomNum);
return randomNum;
}
private boolean isThisRandomNumberExistInAlreadyFoundRandomNumbersArray(int number, int[] numberArr, int numberArrSize) {
for (int j = 0 ; j < numberArrSize ; j ++) {
System.out.println("# Comparing random number: " + number + " and " + (j+1) + ". random number selected earlier: " + numberArr[j]);
if (number == numberArr[j])
return true;
}
return false;
}
private void setMemTiles(int[] randomNums, boolean showMemTiles) {
centerPanel.removeAll();
memTileCoordinates = new int[randomNums.length][2];
for (int i = 1 ; i <= memGridDimY ; i ++) {
for (int j = 1 ; j <= memGridDimX ; j ++) {
int rnX = -1;
int rnY = -1;
boolean isMemTile = false;
for (int k = 0 ; k < randomNums.length ; k ++) {
int rn = randomNums[k];
if (rn % memGridDimX == 0) {
rnY = rn / memGridDimX;
rnX = memGridDimX;
} else {
rnY = rn / memGridDimX + 1;
rnX = rn % memGridDimX;
}
if (i == 1 && j == 1 && !showMemTiles) { //do it once
System.out.println("********* ************");
System.out.println("Random label number: " + rn + " and it's position in the grid: " + rnX + "," + rnY);
memTileCoordinates[k][0] = rnX;
memTileCoordinates[k][1] = rnY;
System.out.println("> Memory Tiles coordinates: " + memTileCoordinates[k][0] + "," + memTileCoordinates[k][1]);
System.out.println("> Memory Tiles length: " + memTileCoordinates.length);
System.out.println("********* ************");
}
if (rnX == j && rnY == i)
isMemTile = true;
}
memTile = new JButton();
if (isMemTile) {
update(getGraphics());
if (showMemTiles) {
memTile.setBackground(Color.red);
System.out.println("%%%% PAINTING MEM TILES IN RED %%%%");
} else
memTile.setBackground(Color.white);
update(getGraphics());
} else
memTile.setBackground(Color.white);
if (!showMemTiles) // we listen actions after the memory tiles disappears
memTile.addActionListener(this);
centerPanel.add(memTile);
update(getGraphics());
}
}
setJPanelSettings();
}
private void setJPanelSettings() {
setSize(memGridDimX * memGridWidthX, memGridDimY * memGridWidthY);
setTitle("Memory :: " + memGridDimX + "x" + memGridDimY);
setResizable(false);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setVisible(true);
}
private void waitALittleBit(long waitingMillisSeconds) {
long t0 = System.currentTimeMillis();
long t1 ;
do {
t1 = System.currentTimeMillis();
} while (t1 - t0 < waitingMillisSeconds);
}
public static void main(String[] args) {
new Memory(minDimX,minDimY);
}
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(">>> An action came >>>");
JButton memTile = (JButton) e.getSource();
Dimension size = memTile.getSize();
int chosenMemTileDimX = memTile.getX();
int chosenMemTileDimY = memTile.getY();
int chosenMemTilePosX = chosenMemTileDimX / size.width + 1; // we add 1 becausee we present our tile numbers as 1:1, 1:2, ... ( instead of 0:0,0:1, ... )
int chosenMemTilePosY = chosenMemTileDimY / size.height + 1;
System.out.println("Chosen Tile's x dimension: " + chosenMemTileDimX + " ans y dimension: " + chosenMemTileDimY);
System.out.println("Chosen Tile's width: " + size.width + " and height: " + size.height);
System.out.println("Chosen Tile's coordinates: " + chosenMemTilePosX + ", " + chosenMemTilePosY);
boolean tileIsMemTile = false;
System.out.println("Memory Tiles Coordinates: ");
for (int i = 0 ; i < memTileCoordinates.length ; i ++) {
int memTileDimX = memTileCoordinates[i][0];
int memTileDimY = memTileCoordinates[i][1];
System.out.println("x: " + memTileDimX + ", y: " + memTileDimY);
if (chosenMemTilePosX == memTileDimX && chosenMemTilePosY == memTileDimY)
tileIsMemTile = true;
}
if (tileIsMemTile) {
System.out.println("!!! Right Tile !!!");
memTile.setBackground(Color.red);
numberOfMemTilesToGuess -= 1;
System.out.println("It rest " + numberOfMemTilesToGuess + " tiles to guess");
} else {
System.out.println("!!! Wrong Tile !!!");
Icon falseTileIcon;
try {
falseTileIcon = new ImageIcon(getClass().getResource("wrong.png"));
if (falseTileIcon.getIconHeight() > 0)
memTile.setIcon(falseTileIcon);
} catch (Exception e1) {
memTile.setBackground(Color.black);
}
update(getGraphics()); // good trick!!
waitALittleBit(1000);
//TODO !!! FAILED IN LEVEL MESSAGE !!!
dispose();
if (memGridDimX == minDimX && ( memGridDimY == minDimY || memGridDimY == minDimY + 1))
new Memory(minDimX, minDimY);
else if (memGridDimX == memGridDimY)
new Memory(memGridDimX - 1, memGridDimY);
else
new Memory(memGridDimX, memGridDimY -1);
}
System.out.println(">>> Action processed >>>");
if (numberOfMemTilesToGuess == 0) {
System.out.println("\n END OF THE LEVEL");
System.out.println("Congratulations, you guessed all the tiles without error !! \n");
dispose();
//TODO !!!! SHOW INTERLEVEL INFORMATION !!!!
if (memGridDimX != maxDimX && memGridDimY != maxDimY) {
if (memGridDimX == memGridDimY)
new Memory(memGridDimX, memGridDimY + 1);
else
new Memory(memGridDimX + 1, memGridDimY);
} else
System.out.println("You have a really good memory my friend!");
}
}}
I used update(getGraphics()); many times, it was just for a test...
Thanks in advance.
You where told in your last posting to NOT use the update() method.
do
{
t1 = System.currentTimeMillis();
}
while (t1 - t0 < waitingMillisSeconds);
Also, never use a tight loop like that. Its just as bad as using Thread.sleep(...);
You where given suggestions in your last posting!
I think you can use LWJGL, which is a java game library.
https://www.lwjgl.org
Some of the issues may be due to the thread on which the various bit of code run. The first time through you are running on the "Main" thread, and to be completely correct calls to update the AWT/Swing GUI should be done using the "Event Dispatcher Thread".
The actionPerformed method is called from the "Event Dispatcher Thread" and so then the second time you call into the constructor for Memory you are in this thread.
Also, aside from the fact you have implemented a delay with a spin-loop, which is not efficient; you should not delay the "Event Dispatcher Thread".
See this short overview: http://www.javamex.com/tutorials/threads/swing_ui.shtml