Skip to content

Commit 91067bf

Browse files
dsward2dsward2
dsward2
authored and
dsward2
committed
In various places where the MP3 encoder bitrate setting is managed, the setting is restricted to valid bitrates defined for the LAME library, which is used by Sox as the current MP3 encoder. Some of the previous settings were invalid (like 3 Kbps), and due to several bugs, in all cases LocalRadio was producing a 320 Kbps stream. The new settings should be much more accurate, and produce streams ranging from 32 Kbps to 320 Kbps. Some cosmetic work remains to be done, like setting the current bitrate in a couple of dropdown menus.
A new button labeled "Open Audio Player Page" is now available in the "Now Playing" page in the web interface. Click or tap that button to load a new standalone audio player page. (If using the LocalRadio web view, right-click in the page and select the "Back" command to exit the standalone player. This will be improved in a future commit.) For radio signals with a low sampling rate, despite some resampling stages in the processing pipeline, the HTML audio element in Safari and the Cocoa WebView have a problem with the stream stalling periodically during playback, rebuffering, and resuming after a few seconds of silence. Other streaming audio clients like Chrome for Android and Mac, QuickTime Player and VNC play the same audio streams smoothly without stalling. Some future work is planned for the audio processing pipeline that might fix the problem in the future, but a workaround is available now: use the "Open Audio Player Page" button described above.
1 parent ede6f3f commit 91067bf

File tree

4 files changed

+100
-43
lines changed

4 files changed

+100
-43
lines changed

LocalRadio/AppDelegate.m

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,6 @@ - (NSString *)webServerControllerURLString
518518
return urlString;
519519
}
520520

521-
522521
//==================================================================================
523522
// checkForProcessConflicts
524523
//==================================================================================
@@ -924,15 +923,18 @@ - (IBAction)updateViews:(id)sender
924923

925924
NSDecimalNumber * mp3SettingsDecimalNumber = [[NSDecimalNumber alloc] initWithString:mp3SettingsString];
926925

927-
NSDecimalNumberHandler * behavior = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundUp scale:0 raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:NO];
928-
NSDecimalNumber * bitrateDecimalNumber = [mp3SettingsDecimalNumber decimalNumberByRoundingAccordingToBehavior: behavior];
926+
//NSDecimalNumberHandler * behavior = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundUp scale:0 raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:NO];
927+
//NSDecimalNumber * bitrateDecimalNumber = [mp3SettingsDecimalNumber decimalNumberByRoundingAccordingToBehavior: behavior];
928+
929+
NSInteger bitrateInteger = [mp3SettingsDecimalNumber integerValue];
930+
NSDecimalNumber * bitrateDecimalNumber = [[NSDecimalNumber alloc] initWithInteger:bitrateInteger];
931+
929932
NSDecimalNumber * encodingQualityDecimalNumber = [mp3SettingsDecimalNumber decimalNumberBySubtracting: bitrateDecimalNumber];
930933
encodingQualityDecimalNumber = [encodingQualityDecimalNumber decimalNumberByMultiplyingByPowerOf10: 1];
931934

932935
NSString * bitrateString = [NSString stringWithFormat:@"%@", bitrateDecimalNumber];
933936
NSString * encodingQualityString = [NSString stringWithFormat:@"%@", encodingQualityDecimalNumber];
934937

935-
float mp3Settings = mp3SettingsDecimalNumber.floatValue;
936938
NSInteger mp3SettingsBitrate = labs(bitrateString.integerValue);
937939
NSInteger mp3SettingsEncodingQuality = labs(encodingQualityString.integerValue);
938940

@@ -962,18 +964,18 @@ - (IBAction)updateViews:(id)sender
962964
encodingQuality = @"Minimum Quality Encoding";
963965
break;
964966
}
965-
966-
NSString * bitrate = @"Unknown Bitrate";
967-
NSString * bitrateMode = @"";
968-
bitrate = [NSString stringWithFormat:@"Constant Bitrate %ld bps", mp3SettingsBitrate];
967+
968+
NSInteger bitrateK = bitrateInteger * 1000;
969+
NSString * bitrate = [NSString stringWithFormat:@"%ld bps", bitrateK];
969970

970971
NSString * mp3SettingsDescription = [NSString stringWithFormat:@"%@, %@", bitrate, encodingQuality];
972+
971973
self.mp3SettingsDescriptionTextField.stringValue = mp3SettingsDescription;
972974
}
973975
else
974976
{
975-
self.mp3SettingsTextField.stringValue = @"16000.2";
976-
self.mp3SettingsDescriptionTextField.stringValue = @"Default Constant Bitrate and Encoding Quality";
977+
self.mp3SettingsTextField.stringValue = @"16.2";
978+
self.mp3SettingsDescriptionTextField.stringValue = @"1600 bps, Default High Encoding Quality";
977979
}
978980
}
979981

@@ -1017,7 +1019,7 @@ - (void)updateConfiguration
10171019
[self.localRadioAppSettings setValue:secondStageSoxFilterString forKey:@"SecondStageSoxFilter"];
10181020

10191021
NSString * constantBitrateString = self.editMP3ConstantPopUpButton.titleOfSelectedItem;
1020-
NSInteger constantBitrateInteger = constantBitrateString.integerValue;
1022+
NSInteger constantBitrateInteger = constantBitrateString.integerValue / 1000;
10211023
NSString * encodingQualityString = self.editMP3EncodingQualityPopUpButton.titleOfSelectedItem;
10221024
NSInteger encodingQualityInteger = encodingQualityString.integerValue;
10231025
NSString * mp3SettingString = [NSString stringWithFormat:@"%ld.%ld", constantBitrateInteger, encodingQualityInteger];

LocalRadio/Base.lproj/MainMenu.xib

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -783,22 +783,28 @@
783783
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="9xJ-Ya-ucR">
784784
<rect key="frame" x="89" y="67" width="103" height="26"/>
785785
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
786-
<popUpButtonCell key="cell" type="push" title="16000 bps" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="J1z-cE-iGO" id="niB-BN-6R8">
786+
<popUpButtonCell key="cell" type="push" title="32000 bps" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="d76-AR-yTO" id="niB-BN-6R8">
787787
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
788788
<font key="font" metaFont="menu"/>
789789
<menu key="menu" id="p4m-Sx-6B4">
790790
<items>
791-
<menuItem title="3000 bps" id="lUD-Lz-igo"/>
792-
<menuItem title="5000 bps" id="N4H-u7-K9H"/>
793-
<menuItem title="7000 bps" id="bSZ-Dh-8Zu"/>
794-
<menuItem title="10000 bps" id="d76-AR-yTO"/>
795-
<menuItem title="12000 bps" id="Sg9-9R-bPc"/>
796-
<menuItem title="16000 bps" state="on" id="J1z-cE-iGO"/>
797-
<menuItem title="24000 bps" id="iIU-Tu-NoR"/>
798-
<menuItem title="32000 bps" id="N0a-GP-NOr"/>
799-
<menuItem title="48000 bps" id="owQ-8p-zMm"/>
800-
<menuItem title="64000 bps" id="chw-qQ-kon"/>
801-
<menuItem title="128000 bps" id="dAm-Zj-dPJ"/>
791+
<menuItem title="8000 bps" id="lUD-Lz-igo"/>
792+
<menuItem title="16000 bps" id="N4H-u7-K9H"/>
793+
<menuItem title="24000 bps" id="bSZ-Dh-8Zu"/>
794+
<menuItem title="32000 bps" state="on" id="d76-AR-yTO"/>
795+
<menuItem title="48000 bps" id="Sg9-9R-bPc"/>
796+
<menuItem title="64000 bps" state="on" id="J1z-cE-iGO"/>
797+
<menuItem title="96000 bps" id="iIU-Tu-NoR"/>
798+
<menuItem title="112000 bps" id="N0a-GP-NOr"/>
799+
<menuItem title="128000 bps" id="owQ-8p-zMm"/>
800+
<menuItem title="192000 bps" id="chw-qQ-kon"/>
801+
<menuItem title="224000 bps" id="dAm-Zj-dPJ"/>
802+
<menuItem title="256000 bps" id="zGn-tX-pO6">
803+
<modifierMask key="keyEquivalentModifierMask"/>
804+
</menuItem>
805+
<menuItem title="320000 bps" id="Gnu-DY-sPj">
806+
<modifierMask key="keyEquivalentModifierMask"/>
807+
</menuItem>
802808
</items>
803809
</menu>
804810
</popUpButtonCell>

LocalRadio/Web/nowplaying.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,13 @@
138138

139139
if (rtlsdr_task_mode == "scan")
140140
{
141+
statusHtml += "<span id='now-playing-details' style='overflow-x: scroll; white-space: nowrap;'>";
142+
141143
statusHtml += "name: ";
142144
statusHtml += station_name;
143145
statusHtml += "<br>";
146+
147+
statusHtml += "</span>"
144148
}
145149

146150
statusHtml += "frequency: ";
@@ -225,6 +229,8 @@ <h3 id="now-playing-name" class="title">%%NOW_PLAYING_NAME%%</h3>
225229
<span id="now-playing-details">
226230
%%NOW_PLAYING_DETAILS%%
227231
</span>
232+
<br>
233+
%%OPEN_AUDIO_PLAYER_PAGE_BUTTON%%
228234
</section>
229235
</div>
230236

LocalRadio/WebServerConnection.m

Lines changed: 63 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,10 @@ - (BOOL)expectsRequestBodyFromMethod:(NSString *)method atPath:(NSString *)path
480480
NSString * nowPlayingDetailsString = [NSString stringWithFormat:@"<br><br>frequency: %@<br>modulation: %@<br>sample rate: %@<br><br>", frequencyString, modulationString, sampleRateString];
481481

482482
[replacementDict setObject:nowPlayingDetailsString forKey:@"NOW_PLAYING_DETAILS"];
483+
484+
NSString * openAudioPlayerPageButtonString = [self generateOpenAudioPlayerPageButtonString];
485+
486+
[replacementDict setObject:openAudioPlayerPageButtonString forKey:@"OPEN_AUDIO_PLAYER_PAGE_BUTTON"];
483487
}
484488
#pragma mark relativePath=nowplayingstatus.html
485489
else if ([relativePath isEqualToString:@"/nowplayingstatus.html"])
@@ -1744,33 +1748,44 @@ - (NSString *)generateMP3BitrateSelectString
17441748
}
17451749

17461750
NSMutableDictionary * bitrateDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:
1747-
@"", @"3000",
1748-
@"", @"5000",
1749-
@"", @"7000",
1750-
@"", @"10000",
1751-
@"", @"12000",
1751+
@"", @"8000",
17521752
@"", @"16000",
17531753
@"", @"24000",
17541754
@"", @"32000",
17551755
@"", @"48000",
17561756
@"", @"64000",
1757+
@"", @"80000",
1758+
@"", @"96000",
1759+
@"", @"112000",
17571760
@"", @"128000",
1761+
@"", @"160000",
1762+
@"", @"192000",
1763+
@"", @"224000",
1764+
@"", @"256000",
1765+
@"", @"320000",
17581766
NULL];
17591767

17601768
[bitrateDictionary setObject:@" selected=\"\"" forKey:currentBitrate];
17611769

1762-
1763-
[resultString appendFormat:@"<option value=\"3000\" %@>Constant Bitrate 3000 bps</option>", [bitrateDictionary objectForKey:@"3000"]];
1764-
[resultString appendFormat:@"<option value=\"5000\" %@>Constant Bitrate 5000 bps</option>", [bitrateDictionary objectForKey:@"5000"]];
1765-
[resultString appendFormat:@"<option value=\"7000\" %@>Constant Bitrate 7000 bps</option>", [bitrateDictionary objectForKey:@"7000"]];
1766-
[resultString appendFormat:@"<option value=\"10000\" %@>Constant Bitrate 10000 bps</option>", [bitrateDictionary objectForKey:@"10000"]];
1767-
[resultString appendFormat:@"<option value=\"12000\" %@>Constant Bitrate 12000 bps</option>", [bitrateDictionary objectForKey:@"12000"]];
1768-
[resultString appendFormat:@"<option value=\"16000\" %@>Constant Bitrate 16000 bps</option>", [bitrateDictionary objectForKey:@"16000"]];
1769-
[resultString appendFormat:@"<option value=\"24000\" %@>Constant Bitrate 24000 bps</option>", [bitrateDictionary objectForKey:@"24000"]];
1770-
[resultString appendFormat:@"<option value=\"32000\" %@>Constant Bitrate 32000 bps</option>", [bitrateDictionary objectForKey:@"32000"]];
1771-
[resultString appendFormat:@"<option value=\"48000\" %@>Constant Bitrate 48000 bps</option>", [bitrateDictionary objectForKey:@"48000"]];
1772-
[resultString appendFormat:@"<option value=\"64000\" %@>Constant Bitrate 64000 bps</option>", [bitrateDictionary objectForKey:@"64000"]];
1773-
[resultString appendFormat:@"<option value=\"128000\" %@>Constant Bitrate 128000 bps</option>", [bitrateDictionary objectForKey:@"128000"]];
1770+
// note from LAME:
1771+
// <bitrate> (bitrate in kbit/s) must be chosen from the following values: 8, 16, 24, 32, 40, 48, 64, 80, 96, 112, 128, 160, 192, 224, 256, or 320.
1772+
1773+
[resultString appendFormat:@"<option value=\"8000\" %@>8000 bps</option>", [bitrateDictionary objectForKey:@"8000"]];
1774+
[resultString appendFormat:@"<option value=\"16000\" %@>16000 bps</option>", [bitrateDictionary objectForKey:@"16000"]];
1775+
[resultString appendFormat:@"<option value=\"24000\" %@>24000 bps</option>", [bitrateDictionary objectForKey:@"24000"]];
1776+
[resultString appendFormat:@"<option value=\"32000\" %@>32000 bps</option>", [bitrateDictionary objectForKey:@"32000"]];
1777+
[resultString appendFormat:@"<option value=\"40000\" %@>40000 bps</option>", [bitrateDictionary objectForKey:@"40000"]];
1778+
[resultString appendFormat:@"<option value=\"48000\" %@>48000 bps</option>", [bitrateDictionary objectForKey:@"48000"]];
1779+
[resultString appendFormat:@"<option value=\"64000\" %@>64000 bps</option>", [bitrateDictionary objectForKey:@"64000"]];
1780+
[resultString appendFormat:@"<option value=\"80000\" %@>80000 bps</option>", [bitrateDictionary objectForKey:@"80000"]];
1781+
[resultString appendFormat:@"<option value=\"96000\" %@>96000 bps</option>", [bitrateDictionary objectForKey:@"96000"]];
1782+
[resultString appendFormat:@"<option value=\"112000\" %@>112000 bps</option>", [bitrateDictionary objectForKey:@"112000"]];
1783+
[resultString appendFormat:@"<option value=\"128000\" %@>128000 bps</option>", [bitrateDictionary objectForKey:@"128000"]];
1784+
[resultString appendFormat:@"<option value=\"160000\" %@>160000 bps</option>", [bitrateDictionary objectForKey:@"160000"]];
1785+
[resultString appendFormat:@"<option value=\"192000\" %@>192000 bps</option>", [bitrateDictionary objectForKey:@"192000"]];
1786+
[resultString appendFormat:@"<option value=\"224000\" %@>224000 bps</option>", [bitrateDictionary objectForKey:@"224000"]];
1787+
[resultString appendFormat:@"<option value=\"256000\" %@>256000 bps</option>", [bitrateDictionary objectForKey:@"256000"]];
1788+
[resultString appendFormat:@"<option value=\"320000\" %@>320000 bps</option>", [bitrateDictionary objectForKey:@"320000"]];
17741789

17751790
return resultString;
17761791
}
@@ -2100,7 +2115,7 @@ - (NSString *)generateAudioPlayerString:(BOOL)userAgentIsLocalRadioApp
21002115
@" onabort='audioPlayerAbort(this);' "
21012116
@" oncanplay='audioPlayerCanPlay(this);' "
21022117
@" oncanplaythrough='audioPlayerCanPlaythrough(this);' "
2103-
@" ondurationchange='audioPlayerCanPlaythrough(this);' "
2118+
@" ondurationchange='audioPlayerDurationChange(this);' "
21042119
@" onemptied='audioPlayerEmptied(this);' "
21052120
@" onended='audioPlayerEnded(this);' "
21062121
@" onerror='audioPlayerError(this, error);' "
@@ -2117,7 +2132,7 @@ - (NSString *)generateAudioPlayerString:(BOOL)userAgentIsLocalRadioApp
21172132
@" onplay='audioPlayerStarted(this);' "
21182133
@" onsuspend='audioPlayerSuspend(this);' "
21192134
@" ontimeupdate='audioPlayerTimeUpdate(this);' "
2120-
@" ontimeupdate='audioPlayerWaiting(this);' ";
2135+
@" onwaiting='audioPlayerWaiting(this);' ";
21212136

21222137
[resultString appendFormat:@"<audio id='audio_element' controls %@ preload=\"none\" src='%@' type='audio/mpeg' %@ title='LocalRadio audio player.'>Your browser does not support the audio element.</audio>\n", autoplayFlag, mp3URLString, audioPlayerJS];
21232138
}
@@ -2161,7 +2176,12 @@ - (void)applyMP3Settings:(NSDictionary *)settingsDictionary
21612176
NSString * bitrate = [settingsDictionary objectForKey:@"bitrate"];
21622177
NSString * encoding_quality = [settingsDictionary objectForKey:@"encoding_quality"];
21632178

2164-
NSString * mp3Setting = [NSString stringWithFormat:@"%@.%@", bitrate, encoding_quality];
2179+
NSInteger bitrateInt = bitrate.integerValue;
2180+
2181+
NSInteger bitrateK = bitrateInt / 1000;
2182+
2183+
//NSString * mp3Setting = [NSString stringWithFormat:@"%@.%@", bitrate, encoding_quality];
2184+
NSString * mp3Setting = [NSString stringWithFormat:@"%ld.%@", bitrateK, encoding_quality];
21652185

21662186
[self performSelectorOnMainThread:@selector(setMP3SettingsTextField:) withObject:mp3Setting waitUntilDone:YES];
21672187

@@ -3558,4 +3578,27 @@ - (NSString *)generateCategorySelectOptions
35583578
return selectOptionsString;
35593579
}
35603580

3581+
3582+
//==================================================================================
3583+
// generateOpenAudioPlayerPageButtonString
3584+
//==================================================================================
3585+
3586+
- (NSString *)generateOpenAudioPlayerPageButtonString
3587+
{
3588+
NSString * hostString = self.appDelegate.localHostString;
3589+
3590+
NSNumber * icecastServerPortNumber = [self.appDelegate.localRadioAppSettings integerForKey:@"IcecastServerPort"];
3591+
NSString * icecastServerMountName = [self.appDelegate.localRadioAppSettings valueForKey:@"IcecastServerMountName"];
3592+
3593+
//NSString * audioURLString = [NSString stringWithFormat:@"window.location='http://%@:%@/%@'", hostString, icecastServerPortNumber, icecastServerMountName];
3594+
//NSString * audioURLString = [NSString stringWithFormat:@"location.href='http://%@:%@/%@'", hostString, icecastServerPortNumber, icecastServerMountName];
3595+
NSString * audioURLString = [NSString stringWithFormat:@"http://%@:%@/%@", hostString, icecastServerPortNumber, icecastServerMountName];
3596+
3597+
//NSString * listenButtonString = [NSString stringWithFormat:@"<button class='button button-primary twelve columns' type='button' onclick=\"%@\" target='_parent'>Open Audio Player Page</button>", audioURLString];
3598+
3599+
NSString * listenButtonString = [NSString stringWithFormat:@"<a href='%@' target='_top'><button class='button button-primary twelve columns' type='button'>Open Audio Player Page</button></a>", audioURLString];
3600+
3601+
return listenButtonString;
3602+
}
3603+
35613604
@end

0 commit comments

Comments
 (0)